Flutter蓝牙设备文档阅读插件flutter_document_reader_btdevice_beta的使用

Flutter蓝牙设备文档阅读插件flutter_document_reader_btdevice_beta的使用

Regula Document Reader (Flutter)

Regula Document Reader SDK允许您读取各种类型的身份证件、护照、驾驶执照、身份证等。所有处理均完全在您的设备上离线完成。没有任何数据离开您的设备。

您可以使用原生相机扫描文档或从图库中选择图像以提取其中的所有数据。

此存储库包含Document Reader API的源代码,以及演示如何与Document Reader库交互的示例应用程序。


目录


如何构建演示应用程序

  1. 访问client.regulaforensics.com以获取试用许可证(regula.license文件)。许可证创建向导将引导您完成必要的步骤。
  2. 使用以下命令下载或克隆此存储库:
    git clone https://github.com/regulaforensics/DocumentReader-Flutter.git
    
  3. regula.license文件复制到example/assets文件夹中。
  4. 在终端中运行以下命令:
    $ cd example
    # 安装包
    $ flutter pub get
    # 检查支持的设备是否正在运行
    $ flutter devices
    # 运行应用程序
    $ flutter run
    
解决许可证问题

如果您在运行应用程序时遇到许可证验证问题,请确保以下几点:

  1. 您使用的操作系统已在许可证中指定(例如Android和/或iOS)。
  2. 许可证有效(未过期)。
  3. 设备上的日期和时间正确。
  4. 您使用的是Document Reader SDK的最新发布版本。
  5. 按照此处所述,将license文件放置在正确的文件夹中。

文档

您可以在此处找到API的文档:https://docs.regulaforensics.com/develop/doc-reader-sdk/mobile/flutter


附加信息

如果您有任何技术问题,请随时通过电子邮件dev.support@regulaforensics.com联系我们或在此处创建问题:https://github.com/regulaforensics/DocumentReader-Flutter/issues

要将我们的SDK集成到您自己的应用程序中,您需要购买商业许可证。


完整示例代码

以下是一个完整的示例代码,展示了如何使用flutter_document_reader_btdevice_beta插件进行蓝牙设备的文档阅读。

import 'dart:convert';
import 'dart:io' as io;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart' show EventChannel, PlatformException, rootBundle;
import 'package:flutter_document_reader_api_beta/document_reader.dart';
import 'package:image_picker/image_picker.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Future<List<String>> getImages() async {
    setStatus("Processing image...");
    List<XFile>? files = await ImagePicker().pickMultiImage();
    List<String> result = [];
    for (XFile file in files)
      result.add(base64Encode(io.File(file.path).readAsBytesSync()));
    return result;
  }

  Object setStatus(String s) => {setState(() => _status = s)};
  String _status = "Loading...";
  bool isReadingRfidCustomUi = false;
  bool isReadingRfid = false;
  String rfidUIHeader = "Reading RFID";
  Color rfidUIHeaderColor = Colors.black;
  String rfidDescription = "Place your phone on top of the NFC tag";
  double rfidProgress = -1;
  var _portrait = Image.asset('assets/images/portrait.png');
  var _docImage = Image.asset('assets/images/id.png');
  List<List<String>> _scenarios = [];
  String _selectedScenario = "Mrz";
  bool _canRfid = false;
  bool _doRfid = false;

  bool getDocumentReaderIsReady = false;
  String btDeviceName = "Regula 0000";
  bool isBleServiceConnected = false;
  bool isBleDeviceReady = false;

  var printError = (Object error) => print((error as PlatformException).message);

  [@override](/user/override)
  void initState() {
    super.initState();
    initPlatformState();
    EventChannel('flutter_document_reader_api/event/completion')
        .receiveBroadcastStream()
        .listen((jsonString) =>
            this.handleCompletion(DocumentReaderCompletion.fromJson(json.decode(jsonString))!));
    EventChannel('flutter_document_reader_api/event/database_progress')
        .receiveBroadcastStream()
        .listen((progress) =>
            setStatus("Downloading database: " + progress + "%"));
    EventChannel('flutter_document_reader_api/event/bleOnServiceConnectedEvent')
        .receiveBroadcastStream()
        .listen((event) => bleOnServiceConnected());
    EventChannel('flutter_document_reader_api/event/bleOnServiceDisconnectedEvent')
        .receiveBroadcastStream()
        .listen((event) => bleOnServiceDisconnected());
    EventChannel('flutter_document_reader_api/event/bleOnDeviceReadyEvent')
        .receiveBroadcastStream()
        .listen((event) => bleOnDeviceReady());
  }

  Future<void> initPlatformState() async {
    print(await DocumentReader.prepareDatabase("FullAuth"));
    setStatus("Database prepared");
  }

  void bleOnServiceConnected() {
    print("bleService connected, searching devices");
    setState(() => isBleServiceConnected = true);
    Future.delayed(const Duration(milliseconds: 7000), () {
      if (!isBleDeviceReady) {
        print("Failed to connect to the torch device");
        setStatus("Failed to connect to the torch device");
      }
    });
  }

  void bleOnServiceDisconnected() {
    print("bleService disconnected");
    setState(() => isBleServiceConnected = false);
  }

  void bleOnDeviceReady() {
    print("device connected, initializing");
    setStatus("Initializing...");
    setState(() => isBleDeviceReady = true);
    DocumentReader.initializeReaderBleDeviceConfig().then((value) => onInitCompleted());
  }

  checkPermissionsAndConnect() {
    DocumentReader.isBlePermissionsGranted().then((granted) {
      if (granted) {
        this.setStatus("Loading...");
        if (!isBleServiceConnected) {
          print("connecting bleService");
          DocumentReader.setConfig({
            "functionality": {
              "useAuthenticator": true,
              "btDeviceName": btDeviceName
            }
          }).then((s) => DocumentReader.startBluetoothService());
        } else
          print("bleService already connected");
      } else
        setStatus("ble permissions denied");
    });
  }

  void addCertificates() async {
    List certificates = [];
    final manifestJson =
        await DefaultAssetBundle.of(context).loadString('AssetManifest.json');
    final certPaths = json
        .decode(manifestJson)
        .keys
        .where((String key) => key.startsWith('assets/certificates'));

    for (var path in certPaths) {
      var findExt = path.split('.');
      var pkdResourceType = 0;
      if (findExt.length > 0)
        pkdResourceType =
            PKDResourceType.getType(findExt[findExt.length - 1].toLowerCase());
      ByteData byteData = await rootBundle.load(path);
      var certBase64 = base64.encode(byteData.buffer
          .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
      certificates
          .add({"binaryData": certBase64, "resourceType": pkdResourceType});
    }

    DocumentReader.addPKDCertificates(certificates)
        .then((value) => print("certificates added"));
  }

  void handleCompletion(DocumentReaderCompletion completion) {
    if (isReadingRfidCustomUi &&
        (completion.action == DocReaderAction.CANCEL ||
            completion.action == DocReaderAction.ERROR)) this.hideRfidUI();
    if (isReadingRfidCustomUi &&
        completion.action == DocReaderAction.NOTIFICATION)
      this.updateRfidUI(completion.results!.documentReaderNotification);
    if (completion.action ==
        DocReaderAction.COMPLETE) if (isReadingRfidCustomUi) if (completion
            .results!.rfidResult !=
        1)
      this.restartRfidUI();
    else {
      this.hideRfidUI();
      this.displayResults(completion.results!);
    }
    else
      this.handleResults(completion.results!);
    if (completion.action == DocReaderAction.TIMEOUT)
      this.handleResults(completion.results!);
    if (completion.action == DocReaderAction.CANCEL ||
        completion.action == DocReaderAction.ERROR) isReadingRfid = false;
  }

  void showRfidUI() {
    setState(() => isReadingRfidCustomUi = true);
  }

  hideRfidUI() {
    this.restartRfidUI();
    DocumentReader.stopRFIDReader();
    setState(() {
      isReadingRfidCustomUi = false;
      rfidUIHeader = "Reading RFID";
      rfidUIHeaderColor = Colors.black;
    });
  }

  restartRfidUI() {
    setState(() {
      rfidUIHeaderColor = Colors.red;
      rfidUIHeader = "Failed!";
      rfidDescription = "Place your phone on top of the NFC tag";
      rfidProgress = -1;
    });
  }

  updateRfidUI(results) {
    if (results.code ==
        ERFIDNotificationCodes.RFID_NOTIFICATION_PCSC_READING_DATAGROUP)
      setState(() => rfidDescription = results.number);
    setState(() {
      rfidUIHeader = "Reading RFID";
      rfidUIHeaderColor = Colors.black;
      rfidProgress = results.value / 100;
    });
    if (Platform.isIOS)
      DocumentReader.setRfidSessionStatus(
          rfidDescription + "\n" + results.value.toString() + "%");
  }

  customRFID() {
    this.showRfidUI();
    DocumentReader.readRFID();
  }

  usualRFID() {
    isReadingRfid = true;
    DocumentReader.startRFIDReader();
  }

  void onInitCompleted() async {
    setStatus("Ready");
    bool canRfid = await DocumentReader.isRFIDAvailableForUse();
    setState(() => _canRfid = canRfid);
    List<List<String>> scenarios = [];
    var scenariosTemp =
        json.decode(await DocumentReader.getAvailableScenarios());
    for (var i = 0; i < scenariosTemp.length; i++) {
      DocumentReaderScenario scenario = DocumentReaderScenario.fromJson(
          scenariosTemp[i] is String
              ? json.decode(scenariosTemp[i])
              : scenariosTemp[i])!;
      scenarios.add([scenario.name!, scenario.caption!]);
    }
    setState(() => _scenarios = scenarios);
    DocumentReader.setConfig({
      "functionality": {
        "videoCaptureMotionControl": true,
        "showCaptureButton": true
      },
      "customization": {
        "showResultStatusMessages": true,
        "showStatusMessages": true
      },
      "processParams": {"scenario": _selectedScenario}
    });
    DocumentReader.setRfidDelegate(RFIDDelegate.NO_PA);
    // addCertificates();
  }

  displayResults(DocumentReaderResults results) async {
    var name = await results.textFieldValueByType(EVisualFieldType.FT_SURNAME_AND_GIVEN_NAMES);
    var doc = await results.graphicFieldImageByType(EGraphicFieldType.GF_DOCUMENT_IMAGE);
    var portrait = await results.graphicFieldImageByType(EGraphicFieldType.GF_PORTRAIT);
    setState(() {
      _status = name ?? "";
      _docImage = Image.asset('assets/images/id.png');
      _portrait = Image.asset('assets/images/portrait.png');
      if (doc != null)
        _docImage = Image.memory(doc.data!.contentAsBytes());
      if (portrait != null)
        _portrait = Image.memory(portrait.data!.contentAsBytes());
    });
  }

  void handleResults(DocumentReaderResults results) {
    if (_doRfid && !isReadingRfid && results.chipPage != 0) {
      usualRFID();
    } else {
      isReadingRfid = false;
      displayResults(results);
    }
  }

  void onChangeRfid(bool? value) {
    setState(() => _doRfid = value! && _canRfid);
    DocumentReader.setConfig({
      "processParams": {"doRfid": _doRfid}
    });
  }

  Widget createImage(String title, double height, double width, ImageProvider image) {
    return Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text(title),
          Image(height: height, width: width, image: image)
        ]);
  }

  Widget createButton(String text, VoidCallback onPress) {
    return Container(
      padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
      transform: Matrix4.translationValues(0, -7.5, 0),
      child: TextButton(
          style: TextButton.styleFrom(
              backgroundColor: Color.fromARGB(50, 10, 10, 10)),
          onPressed: onPress,
          child: Text(text)),
      width: 150,
    );
  }

  Widget _buildRow(int index) {
    Radio radio = new Radio(
        value: _scenarios[index][0],
        groupValue: _selectedScenario,
        onChanged: (value) => setState(() {
              _selectedScenario = value;
              DocumentReader.setConfig({
                "processParams": {"scenario": _selectedScenario}
              });
            }));
    return Container(
        child: ListTile(
            title: GestureDetector(
                onTap: () => radio.onChanged!(_scenarios[index][0]),
                child: Text(_scenarios[index][1])),
            leading: radio),
        padding: EdgeInsets.only(left: 40));
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(title: Center(child: Text(_status))),
          body: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Visibility(
                    visible: isReadingRfidCustomUi,
                    child: Expanded(
                        child: Column(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                          Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: []),
                          Container(
                              child: Text(rfidUIHeader,
                                  textScaleFactor: 1.75,
                                  style: TextStyle(color: rfidUIHeaderColor)),
                              padding: EdgeInsets.only(bottom: 40)),
                          Container(
                              child:
                                  Text(rfidDescription, textScaleFactor: 1.4),
                              padding: EdgeInsets.only(bottom: 40)),
                          FractionallySizedBox(
                              widthFactor: 0.6,
                              child: LinearProgressIndicator(
                                  value: rfidProgress,
                                  minHeight: 10,
                                  valueColor: new AlwaysStoppedAnimation<Color>(
                                      Color(0xFF4285F4)))),
                          TextButton(
                            onPressed: () => hideRfidUI(),
                            child: Text("X"),
                            style: TextButton.styleFrom(
                                padding: EdgeInsets.only(top: 50)),
                          ),
                        ]))),
                Visibility(
                    visible: !isReadingRfidCustomUi,
                    child: Expanded(
                        child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                          Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                createImage(
                                    "Portrait", 150, 150, _portrait.image),
                                createImage("Document image", 150, 200,
                                    _docImage.image),
                              ]),
                          Expanded(
                              child: Container(
                                  color: Color.fromARGB(5, 10, 10, 10),
                                  child: ListView.builder(
                                      itemCount: _scenarios.length,
                                      itemBuilder:
                                          (BuildContext context, int index) =>
                                              _buildRow(index)))),
                          CheckboxListTile(
                              value: _doRfid,
                              onChanged: onChangeRfid,
                              title: Text(
                                  "Process rfid reading ${_canRfid ? "" : "(unavailable)"}")),
                          Padding(
                              padding: EdgeInsets.only(bottom: 16),
                              child: Row(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: [
                                    Text("btDeviceName:  "),
                                    SizedBox(
                                      width: 120,
                                      child: TextFormField(
                                        decoration: InputDecoration(
                                            border: OutlineInputBorder(),
                                            hintText: 'btDeviceName'),
                                        onChanged: (text) {
                                          setState(() => btDeviceName = text);
                                        },
                                        initialValue: btDeviceName,
                                      ),
                                    ),
                                  ])),
                          Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                createButton("Scan document",
                                    () => DocumentReader.showScanner()),
                                createButton("Start service",
                                    () => checkPermissionsAndConnect()),
                              ])
                        ]))),
              ])),
    );
  }
}

更多关于Flutter蓝牙设备文档阅读插件flutter_document_reader_btdevice_beta的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter蓝牙设备文档阅读插件flutter_document_reader_btdevice_beta的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_document_reader_btdevice_beta 是一个用于在 Flutter 应用中读取蓝牙设备文档的插件。它允许开发者通过蓝牙连接设备并读取设备上的文档数据。以下是如何使用该插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 flutter_document_reader_btdevice_beta 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  flutter_document_reader_btdevice_beta: ^版本号

请将 ^版本号 替换为最新的插件版本号。

2. 导入插件

在你的 Dart 文件中导入插件:

import 'package:flutter_document_reader_btdevice_beta/flutter_document_reader_btdevice_beta.dart';

3. 初始化插件

在使用插件之前,通常需要先进行初始化。你可以在 initState 方法中进行初始化。

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  void initState() {
    super.initState();
    FlutterDocumentReaderBtdeviceBeta.initialize();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bluetooth Document Reader'),
        ),
        body: Center(
          child: Text('Bluetooth Document Reader Example'),
        ),
      ),
    );
  }
}

4. 扫描蓝牙设备

你可以使用插件提供的 API 来扫描附近的蓝牙设备。

void scanDevices() async {
  List<BluetoothDevice> devices = await FlutterDocumentReaderBtdeviceBeta.scanDevices();
  for (var device in devices) {
    print('Device Name: ${device.name}, Device ID: ${device.id}');
  }
}

5. 连接蓝牙设备

扫描到设备后,你可以选择连接某个设备。

void connectToDevice(String deviceId) async {
  bool isConnected = await FlutterDocumentReaderBtdeviceBeta.connect(deviceId);
  if (isConnected) {
    print('Connected to device: $deviceId');
  } else {
    print('Failed to connect to device: $deviceId');
  }
}

6. 读取文档

连接成功后,你可以读取设备上的文档。

void readDocument() async {
  String document = await FlutterDocumentReaderBtdeviceBeta.readDocument();
  print('Document Content: $document');
}

7. 断开连接

使用完毕后,记得断开蓝牙连接。

void disconnectDevice() async {
  await FlutterDocumentReaderBtdeviceBeta.disconnect();
  print('Device disconnected');
}

8. 处理权限

在 Android 和 iOS 上,使用蓝牙功能可能需要特定的权限。确保你在 AndroidManifest.xmlInfo.plist 中添加了必要的权限。

Android

AndroidManifest.xml 中添加以下权限:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

iOS

Info.plist 中添加以下权限:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>We need access to Bluetooth to connect to your device.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>We need access to Bluetooth to connect to your device.</string>

9. 处理错误

在实际使用中,可能会遇到各种错误,例如设备未找到、连接失败等。确保你处理了这些错误情况。

void connectToDevice(String deviceId) async {
  try {
    bool isConnected = await FlutterDocumentReaderBtdeviceBeta.connect(deviceId);
    if (isConnected) {
      print('Connected to device: $deviceId');
    } else {
      print('Failed to connect to device: $deviceId');
    }
  } catch (e) {
    print('Error connecting to device: $e');
  }
}

10. 完整示例

以下是一个完整的示例,展示了如何使用 flutter_document_reader_btdevice_beta 插件扫描、连接、读取文档并断开连接。

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

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

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  void initState() {
    super.initState();
    FlutterDocumentReaderBtdeviceBeta.initialize();
  }

  void scanDevices() async {
    List<BluetoothDevice> devices = await FlutterDocumentReaderBtdeviceBeta.scanDevices();
    for (var device in devices) {
      print('Device Name: ${device.name}, Device ID: ${device.id}');
    }
  }

  void connectToDevice(String deviceId) async {
    try {
      bool isConnected = await FlutterDocumentReaderBtdeviceBeta.connect(deviceId);
      if (isConnected) {
        print('Connected to device: $deviceId');
      } else {
        print('Failed to connect to device: $deviceId');
      }
    } catch (e) {
      print('Error connecting to device: $e');
    }
  }

  void readDocument() async {
    String document = await FlutterDocumentReaderBtdeviceBeta.readDocument();
    print('Document Content: $document');
  }

  void disconnectDevice() async {
    await FlutterDocumentReaderBtdeviceBeta.disconnect();
    print('Device disconnected');
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bluetooth Document Reader'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                onPressed: scanDevices,
                child: Text('Scan Devices'),
              ),
              ElevatedButton(
                onPressed: () => connectToDevice('deviceId'),
                child: Text('Connect to Device'),
              ),
              ElevatedButton(
                onPressed: readDocument,
                child: Text('Read Document'),
              ),
              ElevatedButton(
                onPressed: disconnectDevice,
                child: Text('Disconnect Device'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
回到顶部