在 Flutter 中扫描图像并创建二维码时遇到问题

问题描述 投票:0回答:1

我在我的应用程序中使用

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()};
  }
flutter qr-code google-mlkit scanning
1个回答
0
投票

问题可能与 QR 码的创建方式有关,例如图像质量、大小或编码格式。

我可以建议您查看 y.gy (https://app.y.gy),而不是手动创建 QR 码吗?它允许您通过 API 生成优化的 QR 码。此外,您可以批量创建多个二维码,跟踪其性能,甚至阻止机器人滥用您的代码。

© www.soinside.com 2019 - 2024. All rights reserved.