Flutter设备唯一标识插件ad_hoc_ident的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter设备唯一标识插件ad_hoc_ident的使用

ad_hoc_ident 插件提供了从NFC标签和MRZ文档中提取标识符的功能。本指南将帮助您了解如何在Flutter应用中使用该插件。

特性

ad_hoc_ident 包包含三个域包。每个域包都有相应的实现包:

  • 主域:

    • ad_hoc_ident
    • 哈希算法: ad_hoc_ident_util_crypto
    • 可读伪名生成: ad_hoc_ident_util_readable_pseudonym
    • 后台隔离: ad_hoc_ident_util_flutter
  • NFC域:

    • ad_hoc_ident_nfc
    • 基于流的输入: ad_hoc_ident_nfc_scanner_nfc_manager
    • 基于轮询的输入: ad_hoc_ident_nfc_scanner_flutter_nfc_kit
    • EMV卡号检测: ad_hoc_ident_nfc_detect_emv
  • OCR域:

    • ad_hoc_ident_ocr
    • 基本相机输入: ad_hoc_ident_ocr_camera
    • 基于camerawesome包的相机输入: ad_hoc_ident_ocr_camerawesome
    • 基于google_mlkit_text_recognition的文本提取: ad_hoc_ident_ocr_extract_google
    • 基于flutter_vision(Tesseract)的文本提取: ad_hoc_ident_ocr_extract_tesseract
    • 基于mrz_parser的MRZ文档类型和编号检测: ad_hoc_ident_ocr_parse_mrz

开始使用

在您的应用的pubspec.yaml文件中添加主域包和其他需要的功能包:

dependencies:
  ad_hoc_ident: ^x.x.x
  ad_hoc_ident_nfc: ^x.x.x
  ad_hoc_ident_ocr: ^x.x.x
  ...

使用示例

以下是一个完整的示例代码,展示了如何使用ad_hoc_ident插件来获取设备的唯一标识符。

import 'dart:async';
import 'dart:convert';
import 'dart:math';

import 'package:ad_hoc_ident/ad_hoc_ident.dart';
import 'package:ad_hoc_ident_nfc/ad_hoc_ident_nfc.dart';
import 'package:ad_hoc_ident_nfc_detect_emv/ad_hoc_ident_nfc_detect_emv.dart';
import 'package:ad_hoc_ident_nfc_scanner_nfc_manager/ad_hoc_ident_nfc_scanner_nfc_manager.dart';
import 'package:ad_hoc_ident_ocr/ad_hoc_ident_ocr.dart';
import 'package:ad_hoc_ident_ocr_camerawesome/ad_hoc_ident_ocr_camerawesome.dart';
import 'package:ad_hoc_ident_ocr_extract_google/ad_hoc_ident_ocr_extract_google.dart';
import 'package:ad_hoc_ident_ocr_parse_mrz/ad_hoc_ident_ocr_parse_mrz.dart';
import 'package:ad_hoc_ident_util_crypto/ad_hoc_ident_util_crypto.dart';
import 'package:ad_hoc_ident_util_flutter/ad_hoc_ident_util_flutter.dart';
import 'package:ad_hoc_ident_util_readable_pseudonym/ad_hoc_ident_util_readable_pseudonym.dart';
import 'package:async/async.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';

import 'ad_hoc_identity_display.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final MemoryPseudonymStorage _storage = PseudonymStorage.memory();

  late final NfcScanner _nfcScanner;
  late final AdHocIdentityScanner<OcrImage> _ocrScanner;
  late final OcrTextExtractor _extractor;
  late final StreamController<OcrImage> _cameraStreamController;

  Stream<AdHocIdentity?>? _identityStream;
  bool _showCamera = true;

  void _toggleCamera() => setState(() {
        _showCamera = !_showCamera;
      });

  [@override](/user/override)
  void initState() {
    final securePepper = _initPepper();
    final encrypter = WordPseudonymEncrypter(
            innerEncrypter: CryptoAdHocIdentityEncrypter.sha512,
            storage: _storage)
        // 在生产代码中,你可能希望添加一个盐并混淆类型
        // .secureType()
        // .withSalt((identity) => 'someSecurelyCreatedOrFetchedSalt')
        .withPepper(securePepper);

    final nfcDetector = AdHocIdentityDetector.fromList([
      // 保持UID检测器在首位,
      // 因为它会重启NFC适配器,
      // 导致之前的所有适配器重新评估。
      NfcDetectorUid(restartTimeout: const Duration(seconds: 1)),
      NfcDetectorEmv(),
    ]);
    _nfcScanner = _initNfcManagerScanner(nfcDetector, encrypter);
    // _nfcScanner = _initNfcKitScanner(nfcDetector, encrypter);

    // 如果在启动时NFC不可用,则此实现需要重新启动应用。
    // 在生产环境中,你可能希望在NFC状态变为可用之前重复检查其状态。
    _nfcScanner.isAvailable().then((available) {
      if (available) {
        _nfcScanner.start();
      }
    });

    _cameraStreamController = StreamController.broadcast();

    // 将Google提取器包装在一个Future中,以便更容易地切换引擎。
    Future.value(GoogleOcrTextExtractor()).then(
      // TesseractOcrTextExtractor.fromFile().then(
      (extractor) {
        _extractor = extractor;
        _ocrScanner = AdHocIdentityScanner(
          inputStream: _cameraStreamController.stream,
          // 使用后台检测器将OCR卸载到另一个隔离区
          detector: BackgroundIdentityDetector(
            OcrIdentityDetector(
              extractor: extractor,
              parser: MrzTextIdentityParser(),
            ),
          ),
          debounce: true,
          encrypter: encrypter,
        );

        final stream = StreamGroup.merge([
          _nfcScanner.stream,
          _ocrScanner.stream.whereNotNull(),
        ]).distinct().shareValue().asBroadcastStream();

        if (mounted) {
          setState(() {
            _identityStream = stream;
          });
        }
      },
    );

    super.initState();
  }

  NfcScanner _initNfcManagerScanner(AdHocIdentityDetector<NfcTag> detector,
      AdHocIdentityEncrypter encrypter) {
    final unhandledScanner = NfcManagerNfcScanner(
        detector: detector,
        preferredTagTypes: [
          PlatformTagType.isoDep, // 优先选择isoDep以支持EMV功能
        ],
        encrypter: encrypter);
    return unhandledScanner.handle<FirstScanException>(
        FirstScanException.createDefaultHandler(unhandledScanner));
  }

  // NfcScanner _initNfcKitScanner(AdHocIdentityDetector<NfcTag> detector,
  //     AdHocIdentityEncrypter encrypter) {
  //   final unhandledScanner = NfcKitNfcScanner.repeatPolling(
  //       detector: detector,
  //       idleDuration: const Duration(milliseconds: 100),
  //       encrypter: encrypter);
  //   return unhandledScanner.handle<FirstScanException>(
  //     (error, stackTrace) {
  //       //忽略错误
  //     },
  //   );
  // }

  String _initPepper() {
    // 在生产环境中,
    // 应将pepper管理在安全存储或外部API中
    final rng = Random.secure();
    final List<int> securePepperBytes = [];
    for (var i = 0; i < 32; i++) {
      final byte = rng.nextInt(256);
      securePepperBytes.add(byte);
    }
    return base64Encode(securePepperBytes);
  }

  [@override](/user/override)
  void dispose() {
    _cameraStreamController.close();
    _nfcScanner.stop();
    _extractor.close();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    const waitingMessage = '请扫描NFC标签或护照。'
        '在扫描NFC标签前,请关闭相机顶部右侧按钮。'
        '在相机激活时使用NFC可能导致某些设备崩溃。';
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Ad hoc ident'),
          actions: [
            Padding(
              padding: const EdgeInsets.all(5),
              child: _RestartButton(
                restart: () async {
                  final available = await _nfcScanner.isAvailable();
                  if (available) {
                    _nfcScanner.restart();
                  }
                },
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(5),
              child: IconButton(
                onPressed: _toggleCamera,
                icon: Icon(_showCamera ? Icons.videocam_off : Icons.videocam),
              ),
            ),
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(5),
          child: Flex(
            direction: MediaQuery.orientationOf(context) == Orientation.portrait
                ? Axis.vertical
                : Axis.horizontal,
            children: [
              Expanded(
                flex: 2,
                child: Card(
                  shape: const RoundedRectangleBorder(
                    borderRadius: BorderRadius.all(Radius.circular(5)),
                    side: BorderSide(
                      color: Colors.black,
                      width: 2,
                    ),
                  ),
                  clipBehavior: Clip.antiAlias,
                  child: AspectRatio(
                    aspectRatio: 4 / 3,
                    child: _showCamera
                        ? CamerawesomeAdapter(
                            onImage: _cameraStreamController.add)
                        // ? CameraView(
                        //     onImage: _cameraStreamController.add,
                        //     imageFormatGroupName: 'jpeg', // 'nv21',
                        //   )
                        : TextButton.icon(
                            onPressed: _toggleCamera,
                            label: const Text('启用相机'),
                            icon: const Icon(Icons.videocam),
                          ),
                  ),
                ),
              ),
              const SizedBox.square(
                dimension: 10,
              ),
              Flexible(
                flex: 1,
                child: Center(
                  child: Padding(
                    padding: const EdgeInsets.all(15),
                    child: _identityStream != null
                        ? AdHocIdentityDisplay.fromStream(
                            stream: _identityStream!,
                            nullMessage: '未检测到身份。',
                            waitingMessage: waitingMessage,
                            errorBuilder: (error, _) => Text(error.toString()),
                          )
                        : const Text(waitingMessage),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _RestartButton extends StatelessWidget {
  final Future<void> Function() restart;

  const _RestartButton({required this.restart});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return IconButton(
      onPressed: () async {
        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
          content: Text('尝试重新连接NFC...'),
        ));
        await restart();
      },
      icon: const Icon(Icons.nfc),
    );
  }
}

更多关于Flutter设备唯一标识插件ad_hoc_ident的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter设备唯一标识插件ad_hoc_ident的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用ad_hoc_ident插件来获取设备唯一标识的示例代码。这个插件提供了一种生成设备唯一标识符的方法,可以用于广告跟踪、用户识别等场景。

首先,你需要在你的pubspec.yaml文件中添加ad_hoc_ident依赖:

dependencies:
  flutter:
    sdk: flutter
  ad_hoc_ident: ^x.y.z  # 请将x.y.z替换为当前最新版本号

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

接下来,在你的Flutter应用中,你可以使用以下代码来获取设备的唯一标识符:

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

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String? deviceIdentifier;

  @override
  void initState() {
    super.initState();
    _getDeviceIdentifier();
  }

  Future<void> _getDeviceIdentifier() async {
    try {
      String id = await AdHocIdent.getAdHocId;
      setState(() {
        deviceIdentifier = id;
      });
    } catch (e) {
      print("Error getting device identifier: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Device Identifier Example'),
        ),
        body: Center(
          child: Text(
            deviceIdentifier ?? 'Loading...',
            style: TextStyle(fontSize: 24),
          ),
        ),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. pubspec.yaml中添加ad_hoc_ident依赖。
  2. 在主应用文件中(通常是main.dart),导入ad_hoc_ident包。
  3. 创建一个Flutter应用,并在initState方法中调用_getDeviceIdentifier函数来获取设备唯一标识符。
  4. 使用AdHocIdent.getAdHocId异步方法来获取设备标识符,并在获取成功后更新UI。

请注意,设备唯一标识符的生成可能会受到各种因素的影响,比如设备的重置、操作系统的更新等,这可能会导致标识符的变化。因此,在使用这类标识符时,请确保你了解这些潜在的限制。

此外,确保在实际发布应用时遵守相关的隐私政策和法规,特别是在处理用户数据时。

回到顶部