我在我的应用程序中使用
google_ml_kit
来读取图像和扫描二维码,它对于读取二维码效果很好。但是,我在生成二维码时遇到问题。有时,生成的二维码是有效且可扫描的,而有时则不然。当我从图库中选择图像时,二维码扫描仪有时无法检测到二维码或成功扫描并提供结果。 QR 码生成中的这种不一致导致了可靠扫描的问题。
这是屏幕代码:
class QRCodeGeneratorScreen extends StatelessWidget {
final qrCodeController = Get.put(QRCodeController());
QRCodeGeneratorScreen({super.key});
void _showQRCodeGeneratorDialog(BuildContext context) {
final String data = qrCodeController.data.value;
if (data.isNotEmpty) {
showDialog(
context: context,
builder: (BuildContext context) {
return QRCodeGeneratorDialog(
title: 'Your Generated QR Code',
data: data,
);
},
);
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('No QR code data to display'),
backgroundColor: Colors.black38,
));
}
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white,
appBar: const CustomAppBar(
title: 'QR Code Generator',
leftBottomRadius: 50,
rightBottomRadius: 50,
height: 100,
showBackButton: true,
centerTitle: false,
backgroundColor: Colors.blue,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
labelText: 'Enter data for QR code',
border: OutlineInputBorder(),
),
onChanged: (value) {
qrCodeController.updateData(value);
},
),
const SizedBox(height: 20),
Obx(() {
final String data = Uri.encodeComponent(qrCodeController.data.value.trim());
return Center(
child: data.isNotEmpty
? Column(
mainAxisSize: MainAxisSize.min,
children: [
QrImageView(
data: data,
version: QrVersions.auto,
size: 200.0,
backgroundColor: Colors.white,
errorCorrectionLevel: QrErrorCorrectLevel.M, // Adjusted to Medium level
),
SizedBox(height: size.height * 0.02),
SizedBox(
width: size.width * 0.5,
child: ElevatedButton(
onPressed: () =>
_showQRCodeGeneratorDialog(context),
child: const Text(
'Show QR Code',
style: TextStyle(color: Colors.white),
),
),
),
],
)
: const Text('Enter data to generate QR code'),
);
}),
],
),
),
bottomNavigationBar: const CustomBottomNavigationBar(),
);
}
}
这是我将其保存到图库中的 QR 对话框:
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:io';
import '../history_screens/showCustomSnackBar.dart';
import 'package:path/path.dart' as path;
class QRCodeGeneratorDialog extends StatefulWidget {
final String title;
final String data;
const QRCodeGeneratorDialog(
{super.key, required this.title, required this.data});
@override
_QRCodeGeneratorDialogState createState() => _QRCodeGeneratorDialogState();
}
class _QRCodeGeneratorDialogState extends State<QRCodeGeneratorDialog> {
String? savedImagePath;
Future<void> _launchURL() async {
final Uri url = widget.data.startsWith('http')
? Uri.parse(widget.data)
: Uri.parse('https://${widget.data}');
if (await canLaunchUrl(url)) {
await launchUrl(url);
} else {
throw 'Could not launch $url';
}
}
Future<void> _saveImage() async {
try {
final qrValidationResult = QrValidator.validate(
data: widget.data,
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.Q,
);
if (qrValidationResult.status == QrValidationStatus.valid) {
final qrPainter = QrPainter(
data: widget.data,
version: QrVersions.auto,
gapless: true,
errorCorrectionLevel: QrErrorCorrectLevel.Q,
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.square,
color: Color(0xFF000000),
),
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.square,
color: Color(0xFF000000),
),
);
final pictureRecorder = PictureRecorder();
final canvas = Canvas(pictureRecorder);
const double size = 600.0; // Ensure the QR code is rendered at a high resolution
// Background color
final paint = Paint()..color = Colors.white;
canvas.drawRect(const Rect.fromLTWH(0, 0, size, size), paint);
// Render the QR code
qrPainter.paint(canvas, const Size(size, size));
// Convert the QR code to an image
final image = await pictureRecorder.endRecording().toImage(size.toInt(), size.toInt());
final byteData = await image.toByteData(format: ImageByteFormat.png);
final bytes = byteData!.buffer.asUint8List();
// Define the path to the internal storage/pictures/qrcode directory
final directory = Directory('/storage/emulated/0/Pictures/QRCode');
if (!await directory.exists()) {
await directory.create(recursive: true); // Create the directory if it doesn't exist
}
// Create a unique file name
final imagePath = path.join(directory.path, 'qr_code_${DateTime.now().millisecondsSinceEpoch}.png');
final file = File(imagePath);
await file.writeAsBytes(bytes);
// Make the file visible to the gallery and other apps
final result = await ImageGallerySaver.saveFile(file.path);
Get.log('save path: ${file.path}');
if (result['isSuccess']) {
showCustomSnackBar(Get.context!, 'Saved', 'Image saved to QRCode directory');
} else {
showCustomSnackBar(Get.context!, 'Error', 'Error saving image');
}
} else {
showCustomSnackBar(Get.context!, 'Error', 'Invalid QR code data');
}
} catch (e) {
if (kDebugMode) {
print(e);
}
showCustomSnackBar(Get.context!, 'Error', 'Failed to capture image');
}
}
Widget _buildIconButton(
BuildContext context, VoidCallback onPressed, IconData icon) {
return Container(
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(10),
),
child: IconButton(
onPressed: onPressed,
icon: Icon(icon),
),
);
}
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
child: Text(
widget.title,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: QrImageView(
data: widget.data,
version: QrVersions.auto,
size: 200.0,
backgroundColor: Colors.white,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildIconButton(
context, _launchURL, Icons.chrome_reader_mode_outlined),
SizedBox(width: size.width * 0.02),
_buildIconButton(
context,
() {
Clipboard.setData(ClipboardData(text: widget.data));
showCustomSnackBar(
context, 'Copied', 'Text copied to clipboard');
},
Icons.copy,
),
SizedBox(width: size.width * 0.02),
_buildIconButton(context, _saveImage, Icons.save),
],
),
const SizedBox(height: 16),
],
),
);
}
}
这是控制器代码:
class QRCodeController extends GetxController {
final GlobalKey qrKey = GlobalKey(debugLabel: 'GenScreenQR');
var data = ''.obs; // Observable for QR code data
void updateData(String newData) {
data.value = newData;
}
}
这是选择图像和处理图像的代码:
Future<void> pickImage() async {
try {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
final imagePath = pickedFile.path;
Get.log('Selected image path: $imagePath');
final file = File(imagePath);
if (await file.exists()) {
selectedImage.value = file;
} else {
Get.log('File does not exist at the path: $imagePath');
}
} else {
Get.log('No image selected.');
}
} catch (e) {
Get.log('Error picking or processing image: $e');
}
}
Future<void> processImageForQRCode(String imagePath) async {
try {
final file = File(imagePath);
if (!await file.exists()) {
throw 'File does not exist at the specified path.';
}
Get.log('Processing image at path: $imagePath');
final inputImage = InputImage.fromFilePath(imagePath);
// Log the image metadata if possible
final imageInfo = await _getImageInfo(imagePath);
Get.log('Image info: $imageInfo');
const int maxRetries = 3;
int attempts = 0;
List<Barcode> barcodes = [];
while (barcodes.isEmpty && attempts < maxRetries) {
attempts++;
barcodes = await barcodeScanner.processImage(inputImage);
Get.log(
'Attempt $attempts: Number of barcodes detected: ${barcodes.length}');
if (barcodes.isEmpty && attempts < maxRetries) {
await Future.delayed(const Duration(milliseconds: 500));
}
}
if (barcodes.isEmpty) {
Get.log('No QR Code detected.');
Get.snackbar(
'No QR Code Detected',
'No QR code was detected in the image.',
snackPosition: SnackPosition.BOTTOM,
);
} else {
final Set<String> processedCodes = {};
for (Barcode barcode in barcodes) {
final String? qrCodeValue = barcode.rawValue;
Get.log('Found QR Code value: $qrCodeValue');
if (qrCodeValue != null && !processedCodes.contains(qrCodeValue)) {
processedCodes.add(qrCodeValue);
if (!scanHistory.contains(qrCodeValue)) {
scanHistory.add(qrCodeValue);
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setStringList('scanHistory', scanHistory.toList());
Get.log('QR Code Result: $qrCodeValue');
Get.snackbar(
'QR Code Scanned',
qrCodeValue,
snackPosition: SnackPosition.BOTTOM,
);
await Future.delayed(const Duration(milliseconds: 300));
Get.offAll(() => HistoryScreen());
}
}
}
}
} catch (e) {
Get.log('Error processing the image: $e');
Get.snackbar(
'Error',
'An error occurred while processing the image.',
snackPosition: SnackPosition.BOTTOM,
);
}
}
Future<Map<String, dynamic>> _getImageInfo(String imagePath) async {
// Implement a method to retrieve and return image metadata if necessary
return {'size': await File(imagePath).length()};
}
问题可能与 QR 码的创建方式有关,例如图像质量、大小或编码格式。
我可以建议您查看 y.gy (https://app.y.gy),而不是手动创建 QR 码吗?它允许您通过 API 生成优化的 QR 码。此外,您可以批量创建多个二维码,跟踪其性能,甚至阻止机器人滥用您的代码。