Flutter二维码扫描插件fepe_qr_code_scanner的使用

项目状态

Project in Maintenance Mode Only
由于此插件所依赖的基础框架(Android上的zxing和iOS上的MTBBarcodeScanner)均已停止维护,因此此插件目前仅处于维护模式。只有在修复错误和进行小范围增强时才会考虑更新。

我正在开发一个新的插件mobile_scanner,它使用了MLKit的最新版本来检测条形码和二维码。在Android上,它还使用了CameraX的最新版本,在iOS上则使用了AVFoundation以实现最佳的相机性能。


QR Code Scanner

pub package

截图

Android

iOS


扫描二维码获取结果

当二维码被识别时,识别出的文本将存储在类型为Barcoderesult变量中,其中包含输出文本属性code(类型为String)和扫描码类型属性format(类型为枚举BarcodeFormat)。

class _QRViewExampleState extends State<QRViewExample> {
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
  Barcode? result;
  QRViewController? controller;

  [@override](/user/override)
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      controller?.pauseCamera(); // 暂停Android摄像头
    } else if (Platform.isIOS) {
      controller?.resumeCamera(); // 恢复iOS摄像头
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(
            flex: 5,
            child: QRView(
              key: qrKey,
              onQRViewCreated: _onQRViewCreated,
            ),
          ),
          Expanded(
            flex: 1,
            child: Center(
              child: (result != null)
                  ? Text(
                      'Barcode Type: ${describeEnum(result!.format)}   Data: ${result!.code}')
                  : Text('Scan a code'),
            ),
          )
        ],
      ),
    );
  }

  void _onQRViewCreated(QRViewController controller) {
    this.controller = controller;
    controller.scannedDataStream.listen((scanData) {
      setState(() {
        result = scanData;
      });
    });
  }

  [@override](/user/override)
  void dispose() {
    controller?.dispose();
    super.dispose();
  }
}

Android集成

为了使用该插件,请更新以下Gradle配置:

  1. android/build.gradle中:

    ext.kotlin_version = '1.5.10'
    classpath 'com.android.tools.build:gradle:4.2.0'
    
  2. android/gradle/wrapper/gradle-wrapper.properties中:

    distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip
    
  3. android/app/build.gradle中:

    minSdkVersion 20
    

警告:如果您使用的是Flutter Beta或Dev通道(如1.25或1.26),可能会遇到以下错误:

java.lang.AbstractMethodError: abstract method "void io.flutter.plugin.platform.PlatformView.onFlutterViewAttached(android.view.View)"

这是一个已知的Flutter Bug,可以通过在gradle.properties文件中添加以下内容解决:

android.enableDexingArtifactTransform=false

iOS集成

为了在iOS上使用该插件,请在Info.plist文件中添加以下内容:

<key>io.flutter.embedded_views_preview</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>

Web集成

web/index.html中添加以下脚本:

<script src="https://cdn.jsdelivr.net/npm/jsqr@1.3.1/dist/jsQR.min.js"></script>

注意:在Web端,仅支持QR码的扫描,其他条形码和二维码无法扫描。此外,Web支持仍处于早期阶段,功能如闪光灯、暂停或恢复尚未实现,且相机预览可能不完全适应周围约束。


切换前后摄像头

默认情况下,使用后置摄像头。切换到前置摄像头:

await controller.flipCamera();

开启/关闭闪光灯

默认情况下,闪光灯关闭。切换闪光灯状态:

await controller.toggleFlash();

暂停/恢复摄像头

暂停摄像头流和扫描器:

await controller.pauseCamera();

恢复摄像头流和扫描器:

await controller.resumeCamera();

示例代码

以下是完整的示例代码,展示了如何使用fepe_qr_code_scanner插件:

import 'dart:developer';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fepe_qr_code_scanner/qr_code_scanner.dart';

void main() => runApp(const MaterialApp(home: MyHome()));

class MyHome extends StatelessWidget {
  const MyHome({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter Demo Home Page')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(
              builder: (context) => const QRViewExample(),
            ));
          },
          child: const Text('qrView'),
        ),
      ),
    );
  }
}

class QRViewExample extends StatefulWidget {
  const QRViewExample({Key? key}) : super(key: key);

  [@override](/user/override)
  State<StatefulWidget> createState() => _QRViewExampleState();
}

class _QRViewExampleState extends State<QRViewExample> {
  Barcode? result;
  QRViewController? controller;
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');

  [@override](/user/override)
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      controller?.pauseCamera();
    }
    controller?.resumeCamera();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(flex: 4, child: _buildQrView(context)),
          Expanded(
            flex: 1,
            child: FittedBox(
              fit: BoxFit.contain,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  if (result != null)
                    Text(
                        'Barcode Type: ${describeEnum(result!.format)}   Data: ${result!.code}')
                  else
                    const Text('Scan a code'),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Container(
                        margin: const EdgeInsets.all(8),
                        child: ElevatedButton(
                            onPressed: () async {
                              await controller?.toggleFlash();
                              setState(() {});
                            },
                            child: FutureBuilder(
                              future: controller?.getFlashStatus(),
                              builder: (context, snapshot) {
                                return Text('Flash: ${snapshot.data}');
                              },
                            )),
                      ),
                      Container(
                        margin: const EdgeInsets.all(8),
                        child: ElevatedButton(
                            onPressed: () async {
                              await controller?.flipCamera();
                              setState(() {});
                            },
                            child: FutureBuilder(
                              future: controller?.getCameraInfo(),
                              builder: (context, snapshot) {
                                if (snapshot.data != null) {
                                  return Text(
                                      'Camera facing ${describeEnum(snapshot.data!)}');
                                } else {
                                  return const Text('loading');
                                }
                              },
                            )),
                      )
                    ],
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Container(
                        margin: const EdgeInsets.all(8),
                        child: ElevatedButton(
                          onPressed: () async {
                            await controller?.pauseCamera();
                          },
                          child: const Text('pause', style: TextStyle(fontSize: 20)),
                        ),
                      ),
                      Container(
                        margin: const EdgeInsets.all(8),
                        child: ElevatedButton(
                          onPressed: () async {
                            await controller?.resumeCamera();
                          },
                          child: const Text('resume', style: TextStyle(fontSize: 20)),
                        ),
                      )
                    ],
                  ),
                ],
              ),
            ),
          )
        ],
      ),
    );
  }

  Widget _buildQrView(BuildContext context) {
    var scanArea = (MediaQuery.of(context).size.width < 400 || MediaQuery.of(context).size.height < 400)
        ? 150.0
        : 300.0;
    return QRView(
      key: qrKey,
      onQRViewCreated: _onQRViewCreated,
      overlay: QrScannerOverlayShape(
          borderColor: Colors.red,
          borderRadius: 10,
          borderLength: 30,
          borderWidth: 10,
          cutOutSize: scanArea),
      onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
    );
  }

  void _onQRViewCreated(QRViewController controller) {
    setState(() {
      this.controller = controller;
    });
    controller.scannedDataStream.listen((scanData) {
      setState(() {
        result = scanData;
      });
    });
  }

  void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
    log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
    if (!p) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('no Permission')),
      );
    }
  }

  [@override](/user/override)
  void dispose() {
    controller?.dispose();
    super.dispose();
  }
}

更多关于Flutter二维码扫描插件fepe_qr_code_scanner的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter二维码扫描插件fepe_qr_code_scanner的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_qr_code_scanner 是一个用于在 Flutter 应用中扫描二维码的插件。它是一个基于 mobile_scanner 的插件,提供了简单易用的 API 来扫描二维码和条形码。

安装

首先,将 flutter_qr_code_scanner 添加到你的 pubspec.yaml 文件中:

dependencies:
  flutter:
    sdk: flutter
  flutter_qr_code_scanner: ^latest_version

然后运行 flutter pub get 来安装依赖。

基本用法

  1. 导入包

    在你的 Dart 文件中导入 flutter_qr_code_scanner

    import 'package:flutter_qr_code_scanner/flutter_qr_code_scanner.dart';
    
  2. 创建 QRCodeScanner 控件

    使用 QRCodeScanner 控件来显示摄像头并扫描二维码:

    class QRScannerScreen extends StatefulWidget {
      @override
      _QRScannerScreenState createState() => _QRScannerScreenState();
    }
    
    class _QRScannerScreenState extends State<QRScannerScreen> {
      final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
      QRViewController? controller;
      String? result;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('QR Code Scanner'),
          ),
          body: Column(
            children: <Widget>[
              Expanded(
                flex: 5,
                child: QRView(
                  key: qrKey,
                  onQRViewCreated: _onQRViewCreated,
                ),
              ),
              Expanded(
                flex: 1,
                child: Center(
                  child: Text('Scan result: ${result ?? "No data"}'),
                ),
              ),
            ],
          ),
        );
      }
    
      void _onQRViewCreated(QRViewController controller) {
        this.controller = controller;
        controller.scannedDataStream.listen((scanData) {
          setState(() {
            result = scanData.code;
          });
        });
      }
    
      @override
      void dispose() {
        controller?.dispose();
        super.dispose();
      }
    }
    
  3. 处理扫描结果

    _onQRViewCreated 方法中,你可以监听 scannedDataStream 来获取扫描到的二维码数据。每当扫描到一个新的二维码时,scannedDataStream 会发出一个包含二维码数据的 Barcode 对象。

  4. 权限处理

    确保你的应用有摄像头的使用权限。你可以在 AndroidManifest.xmlInfo.plist 中添加相应的权限配置。

    Android:

    <uses-permission android:name="android.permission.CAMERA" />
    

    iOS:

    <key>NSCameraUsageDescription</key>
    <string>We need access to your camera to scan QR codes</string>
    
  5. 运行应用

    现在你可以运行你的 Flutter 应用,并使用 QRScannerScreen 来扫描二维码了。

高级用法

  • 控制摄像头闪光灯

    你可以通过 controller 来控制摄像头的闪光灯:

    controller?.toggleFlash();
    
  • 暂停和恢复扫描

    你可以通过 controller 来暂停和恢复扫描:

    controller?.pauseCamera();
    controller?.resumeCamera();
回到顶部