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

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

简介

barcode_scan3 是一个用于扫描二维条形码和QR码的Flutter插件。它封装了两个常用的iOS和Android库:

此插件支持以下功能:

  • 扫描二维条形码
  • 扫描QR码
  • 控制闪光灯
  • 权限处理

使用步骤

Android 配置

在使用插件之前,需要在 AndroidManifest.xml 文件中添加相机权限:

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

此外,此插件使用Kotlin编写,因此需要为项目添加Kotlin支持。编辑项目级别的 build.gradle 文件:

buildscript {
    ext.kotlin_version = '1.3.61'
    // ...
    dependencies {
        // ...
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
// ...

然后编辑应用级别的 build.gradle 文件:

apply plugin: 'kotlin-android'
// ...
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    // ...
}

最后,在 pubspec.yaml 文件中添加依赖:

dependencies:
    # ...
    barcode_scan3: any

运行 flutter packages get 或点击Android Studio中的“Packages get”。

iOS 配置

在iOS上使用时,需要在 Info.plist 文件中添加相机使用描述:

<key>NSCameraUsageDescription</key>
<string>Camera permission is required for barcode scanning.</string>

基本用法

以下是一个简单的示例,展示如何使用 barcode_scan3 插件进行二维码扫描:

import 'package:barcode_scan3/barcode_scan3.dart';

void main() async {
  var result = await BarcodeScanner.scan();

  print(result.type); // 结果类型(barcode, cancelled, failed)
  print(result.rawContent); // 二维码内容
  print(result.format); // 二维码格式(枚举)
  print(result.formatNote); // 如果扫描到未知格式,则包含说明
}

高级用法

可以传递选项来定制扫描行为:

import 'package:barcode_scan3/barcode_scan3.dart';

void main() async {
  var options = ScanOptions(
    strings: {
      'cancel': '取消',
      'flash_on': '打开闪光灯',
      'flash_off': '关闭闪光灯',
    },
    restrictFormat: [BarcodeFormat.qr], // 限制识别的格式
    useCamera: 0, // 使用默认摄像头
    autoEnableFlash: true, // 启动扫描时开启闪光灯
    android: AndroidOptions(
      aspectTolerance: 0.5, // 设置宽容度
      useAutoFocus: true, // 启用自动对焦
    ),
  );

  var result = await BarcodeScanner.scan(options: options);

  // 处理扫描结果
}

支持的选项

以下是 ScanOptions 中支持的选项及其说明:

选项名称 类型 描述 支持平台
strings.cancel String iOS上的取消按钮文本 iOS only
strings.flash_on String 闪光灯开启按钮文本 iOS + Android
strings.flash_off String 闪光灯关闭按钮文本 iOS + Android
restrictFormat BarcodeFormat[] 限制识别的格式 iOS + Android
useCamera int 使用的摄像头索引 iOS + Android
autoEnableFlash bool 启动扫描时是否开启闪光灯 iOS + Android
android.aspectTolerance double 设置宽容度 Android only
android.useAutoFocus bool 是否启用自动对焦 Android only

示例代码

以下是一个完整的示例代码,展示了如何实现一个带有自定义选项的二维码扫描界面:

import 'dart:async';
import 'dart:io' show Platform;

import 'package:barcode_scan3/barcode_scan3.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(const App());

class App extends StatefulWidget {
  const App({Key? key}) : super(key: key);
  [@override](/user/override)
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  ScanResult? scanResult;

  final _flashOnController = TextEditingController(text: '打开闪光灯');
  final _flashOffController = TextEditingController(text: '关闭闪光灯');
  final _cancelController = TextEditingController(text: '取消');

  var _aspectTolerance = 0.00;
  var _numberOfCameras = 0;
  var _selectedCamera = -1;
  var _useAutoFocus = true;
  var _autoEnableFlash = false;

  static final _possibleFormats = BarcodeFormat.values.toList()
    ..removeWhere((e) => e == BarcodeFormat.unknown);

  List<BarcodeFormat> selectedFormats = [..._possibleFormats];

  [@override](/user/override)
  void initState() {
    super.initState();

    Future.delayed(Duration.zero, () async {
      _numberOfCameras = await BarcodeScanner.numberOfCameras;
      setState(() {});
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    final scanResult = this.scanResult;
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('二维码扫描示例'),
          actions: [
            IconButton(
              icon: const Icon(Icons.camera),
              tooltip: '扫描',
              onPressed: _scan,
            )
          ],
        ),
        body: ListView(
          scrollDirection: Axis.vertical,
          shrinkWrap: true,
          children: <Widget>[
            if (scanResult != null)
              Card(
                child: Column(
                  children: <Widget>[
                    ListTile(
                      title: const Text('结果类型'),
                      subtitle: Text(scanResult.type.toString()),
                    ),
                    ListTile(
                      title: const Text('原始内容'),
                      subtitle: Text(scanResult.rawContent),
                    ),
                    ListTile(
                      title: const Text('格式'),
                      subtitle: Text(scanResult.format.toString()),
                    ),
                    ListTile(
                      title: const Text('格式说明'),
                      subtitle: Text(scanResult.formatNote),
                    ),
                  ],
                ),
              ),
            const ListTile(
              title: Text('摄像头选择'),
              dense: true,
              enabled: false,
            ),
            RadioListTile(
              onChanged: (v) => setState(() => _selectedCamera = -1),
              value: -1,
              title: const Text('默认摄像头'),
              groupValue: _selectedCamera,
            ),
            ...List.generate(
              _numberOfCameras,
              (i) => RadioListTile(
                onChanged: (v) => setState(() => _selectedCamera = i),
                value: i,
                title: Text('摄像头 ${i + 1}'),
                groupValue: _selectedCamera,
              ),
            ),
            const ListTile(
              title: Text('按钮文本'),
              dense: true,
              enabled: false,
            ),
            ListTile(
              title: TextField(
                decoration: const InputDecoration(
                  floatingLabelBehavior: FloatingLabelBehavior.always,
                  labelText: '闪光灯开启',
                ),
                controller: _flashOnController,
              ),
            ),
            ListTile(
              title: TextField(
                decoration: const InputDecoration(
                  floatingLabelBehavior: FloatingLabelBehavior.always,
                  labelText: '闪光灯关闭',
                ),
                controller: _flashOffController,
              ),
            ),
            ListTile(
              title: TextField(
                decoration: const InputDecoration(
                  floatingLabelBehavior: FloatingLabelBehavior.always,
                  labelText: '取消',
                ),
                controller: _cancelController,
              ),
            ),
            if (Platform.isAndroid) ...[
              const ListTile(
                title: Text('Android特定选项'),
                dense: true,
                enabled: false,
              ),
              ListTile(
                title: Text(
                  '宽容度 (${_aspectTolerance.toStringAsFixed(2)})',
                ),
                subtitle: Slider(
                  min: -1,
                  max: 1,
                  value: _aspectTolerance,
                  onChanged: (value) {
                    setState(() {
                      _aspectTolerance = value;
                    });
                  },
                ),
              ),
              CheckboxListTile(
                title: const Text('启用自动对焦'),
                value: _useAutoFocus,
                onChanged: (checked) {
                  setState(() {
                    _useAutoFocus = checked!;
                  });
                },
              ),
            ],
            const ListTile(
              title: Text('其他选项'),
              dense: true,
              enabled: false,
            ),
            CheckboxListTile(
              title: const Text('启动时开启闪光灯'),
              value: _autoEnableFlash,
              onChanged: (checked) {
                setState(() {
                  _autoEnableFlash = checked!;
                });
              },
            ),
            const ListTile(
              title: Text('条形码格式'),
              dense: true,
              enabled: false,
            ),
            ListTile(
              trailing: Checkbox(
                tristate: true,
                materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
                value: selectedFormats.length == _possibleFormats.length
                    ? true
                    : selectedFormats.isEmpty
                        ? false
                        : null,
                onChanged: (checked) {
                  setState(() {
                    selectedFormats = [
                      if (checked ?? false) ..._possibleFormats,
                    ];
                  });
                },
              ),
              dense: true,
              enabled: false,
              title: const Text('检测条形码格式'),
              subtitle: const Text(
                '如果全部未选中,则会使用所有可能的平台格式',
              ),
            ),
            ..._possibleFormats.map(
              (format) => CheckboxListTile(
                value: selectedFormats.contains(format),
                onChanged: (i) {
                  setState(() =>
                      selectedFormats.contains(format)
                          ? selectedFormats.remove(format)
                          : selectedFormats.add(format));
                },
                title: Text(format.toString()),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _scan() async {
    try {
      final result = await BarcodeScanner.scan(
        options: ScanOptions(
          strings: {
            'cancel': _cancelController.text,
            'flash_on': _flashOnController.text,
            'flash_off': _flashOffController.text,
          },
          restrictFormat: selectedFormats,
          useCamera: _selectedCamera,
          autoEnableFlash: _autoEnableFlash,
          android: AndroidOptions(
            aspectTolerance: _aspectTolerance,
            useAutoFocus: _useAutoFocus,
          ),
        ),
      );
      setState(() => scanResult = result);
    } on PlatformException catch (e) {
      setState(() {
        scanResult = ScanResult(
          type: ResultType.Error,
          format: BarcodeFormat.unknown,
          rawContent: e.code == BarcodeScanner.cameraAccessDenied
              ? '用户未授予相机权限!'
              : '未知错误: $e',
        );
      });
    }
  }
}

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

1 回复

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


barcode_scan3 是一个用于 Flutter 的二维码和条形码扫描插件。它基于 Android 的 ZXing 和 iOS 的 MTBBarcodeScanner 库。以下是如何在 Flutter 项目中使用 barcode_scan3 插件的步骤。

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 barcode_scan3 依赖:

dependencies:
  flutter:
    sdk: flutter
  barcode_scan3: ^3.0.0

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

2. 配置平台

Android 配置

android/app/src/main/AndroidManifest.xml 文件中添加相机权限:

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

确保 android/app/build.gradle 中的 minSdkVersion 至少为 21:

defaultConfig {
    minSdkVersion 21
    targetSdkVersion 30
    // 其他配置
}

iOS 配置

ios/Runner/Info.plist 文件中添加相机权限描述:

<key>NSCameraUsageDescription</key>
<string>We need access to your camera to scan barcodes.</string>

3. 使用插件

在 Dart 代码中导入 barcode_scan3 插件并使用它来启动扫描。

import 'package:flutter/material.dart';
import 'package:barcode_scan3/barcode_scan3.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BarcodeScanner(),
    );
  }
}

class BarcodeScanner extends StatefulWidget {
  @override
  _BarcodeScannerState createState() => _BarcodeScannerState();
}

class _BarcodeScannerState extends State<BarcodeScanner> {
  String _scanResult = 'Scan a barcode';

  Future<void> _scan() async {
    try {
      final result = await BarcodeScanner.scan();
      setState(() {
        _scanResult = result.rawContent ?? 'No data found';
      });
    } catch (e) {
      setState(() {
        _scanResult = 'Failed to scan: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Barcode Scanner'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(_scanResult),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _scan,
              child: Text('Scan Barcode'),
            ),
          ],
        ),
      ),
    );
  }
}

4. 运行项目

确保你的设备或模拟器已连接,然后运行项目:

flutter run

5. 处理扫描结果

扫描完成后,BarcodeScanner.scan() 方法会返回一个 ScanResult 对象,其中包含扫描到的数据。你可以通过 result.rawContent 获取扫描到的字符串。

6. 自定义配置

barcode_scan3 插件允许你自定义扫描界面的样式和行为。你可以在 BarcodeScanner.scan() 方法中传递 ScanOptions 对象来进行配置。例如:

final result = await BarcodeScanner.scan(
  options: const ScanOptions(
    strings: {
      'cancel': 'Cancel',
      'flash_on': 'Flash On',
      'flash_off': 'Flash Off',
    },
    autoEnableFlash: false,
  ),
);

7. 处理权限

在某些情况下,你可能需要手动请求相机权限。你可以使用 permission_handler 插件来处理权限请求。

import 'package:permission_handler/permission_handler.dart';

Future<void> _requestPermission() async {
  var status = await Permission.camera.status;
  if (!status.isGranted) {
    await Permission.camera.request();
  }
}
回到顶部