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 '../../providers/user_provider.dart'; class UpdateScorePage extends StatefulWidget { const UpdateScorePage({super.key}); @override State createState() => _UpdateScorePageState(); } class _UpdateScorePageState extends State { final TextEditingController _segaIdController = TextEditingController(); final ImagePicker _picker = ImagePicker(); bool _isProcessing = false; String? _statusMessage; File? _selectedImageFile; // 用于预览 // 条码扫描器 (支持 QR Code, Data Matrix, Aztec 等) final BarcodeScanner _barcodeScanner = BarcodeScanner( formats: [BarcodeFormat.qrCode, BarcodeFormat.dataMatrix, BarcodeFormat.aztec], ); @override void dispose() { _segaIdController.dispose(); _barcodeScanner.close(); super.dispose(); } // 1. 选择图片并识别二维码 Future _pickImageAndScan() async { try { final XFile? pickedFile = await _picker.pickImage(source: ImageSource.gallery); if (pickedFile == null) return; setState(() { _isProcessing = true; _statusMessage = "正在识别二维码..."; _selectedImageFile = File(pickedFile.path); }); // 执行二维码识别 final inputImage = InputImage.fromFilePath(pickedFile.path); final List barcodes = await _barcodeScanner.processImage(inputImage); setState(() { _isProcessing = false; }); if (barcodes.isNotEmpty) { // 取第一个识别到的二维码内容 final String code = barcodes.first.rawValue ?? ""; if (code.isNotEmpty) { setState(() { _segaIdController.text = code; _statusMessage = "识别成功: $code"; }); } else { setState(() { _statusMessage = "二维码内容为空"; }); } } else { setState(() { _statusMessage = "未在图中发现二维码,请手动输入"; }); } } catch (e) { setState(() { _isProcessing = false; _statusMessage = "识别失败: $e"; }); } } // 2. 执行隐写上传 Future _handleUpload() async { final segaId = _segaIdController.text.trim(); if (segaId.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("请输入或扫描二维码"), backgroundColor: Colors.orange), ); return; } Uint8List? imageBytes; setState(() { _isProcessing = true; _statusMessage = "正在准备图片..."; }); try { // 策略:优先使用用户选择的截图作为载体。 // 如果用户没选图,或者选图后删除了,我们可以下载默认背景图作为载体。 if (_selectedImageFile != null) { imageBytes = await _selectedImageFile!.readAsBytes(); } else { // 下载默认背景图作为隐写载体 final httpClient = HttpClient(); final request = await httpClient.getUrl(Uri.parse('https://union.godserver.cn/assets/jpeg/20180621142015_5FmGZ-wYXkyL4y.jpeg')); final response = await request.close(); // 辅助函数:将 HttpClientResponse 转为 Uint8List final bytes = []; await for (final chunk in response) { bytes.addAll(chunk); } imageBytes = Uint8List.fromList(bytes); } if (imageBytes == null || imageBytes.isEmpty) { throw Exception("图片数据无效"); } setState(() { _statusMessage = "正在隐写并上传..."; }); // 调用 Provider 上传 final userProvider = context.read(); final result = await userProvider.uploadStegImage(imageBytes, segaId: segaId); setState(() { _isProcessing = false; _statusMessage = "上传成功!"; }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("上传成功: ${result['msg']}"), backgroundColor: Colors.green), ); // 可选:清空表单 // _segaIdController.clear(); // _selectedImageFile = null; } } catch (e) { setState(() { _isProcessing = false; _statusMessage = "上传失败: $e"; }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("错误: $e"), backgroundColor: Colors.red), ); } } } @override Widget build(BuildContext context) { return Scaffold( body: Stack( fit: StackFit.expand, children: [ // 1. 背景图片 Image.network( 'https://union.godserver.cn/assets/jpeg/20180621142015_5FmGZ-wYXkyL4y.jpeg', fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Container(color: Colors.black); }, errorBuilder: (context, error, stackTrace) { return Container(color: Colors.grey[900]); }, ), // 2. 深色遮罩 Container(color: Colors.black.withOpacity(0.7)), // 3. 主体内容 SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 标题 const Text( "更新成绩 / 二维码", style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white, ), ), const SizedBox(height: 30), // 卡片容器 Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white.withOpacity(0.15), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.white.withOpacity(0.2)), ), child: Column( children: [ // 输入框 TextField( controller: _segaIdController, style: const TextStyle(color: Colors.white), decoration: InputDecoration( labelText: "Sega ID / 二维码内容", labelStyle: const TextStyle(color: Colors.white70), hintText: "手动输入或点击下方按钮识别", hintStyle: const TextStyle(color: Colors.white38), prefixIcon: const Icon(Icons.qr_code, color: Colors.white70), enabledBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.white38), borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( borderSide: const BorderSide(color: Colors.white), borderRadius: BorderRadius.circular(12), ), ), ), const SizedBox(height: 20), // 图片预览区域 (如果有) if (_selectedImageFile != null) ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.file( _selectedImageFile!, height: 150, fit: BoxFit.contain, ), ), if (_selectedImageFile != null) const SizedBox(height: 15), // 操作按钮组 Row( children: [ // 识别二维码按钮 Expanded( child: ElevatedButton.icon( onPressed: _isProcessing ? null : _pickImageAndScan, icon: const Icon(Icons.camera_alt), label: const Text("识别截图"), style: ElevatedButton.styleFrom( backgroundColor: Colors.blueAccent, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), const SizedBox(width: 10), // 上传按钮 Expanded( flex: 1, child: ElevatedButton.icon( onPressed: _isProcessing ? null : _handleUpload, icon: _isProcessing ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), ) : const Icon(Icons.cloud_upload), label: Text(_isProcessing ? "处理中..." : "上传"), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), ], ), ], ), ), // 状态提示 if (_statusMessage != null) Padding( padding: const EdgeInsets.only(top: 20), child: Text( _statusMessage!, style: TextStyle( color: _statusMessage!.contains("失败") || _statusMessage!.contains("错误") ? Colors.redAccent : Colors.white70, fontSize: 14, ), textAlign: TextAlign.center, ), ), ], ), ), ), ), ], ), ); } }