Flutter泰国身份证读取插件thai_idcard_reader_flutter的使用

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

Flutter泰国身份证读取插件thai_idcard_reader_flutter的使用

简介

thai_idcard_reader_flutter 是一个用于与ACS ACR39U智能卡读卡器通信以即时读取泰国身份证的插件。

支持

  • Android 5.0 或更新版本
  • iOS 不可用

测试设备

  • ACR39U-NF PocketMate II 智能卡读卡器(USB Type-C)
  • 或任何由ACS制造的产品,如果它们可以正常工作

开始使用

以下是完整的示例代码,展示如何在Flutter应用中使用 thai_idcard_reader_flutter 插件来读取泰国身份证信息。

import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:thai_idcard_reader_flutter/thai_idcard_reader_flutter.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';

void main() {
  Intl.defaultLocale = 'th_TH';
  initializeDateFormatting('th_TH', null);
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ThaiIDCard? _data;
  var _error;
  UsbDevice? _device;
  var _card;
  StreamSubscription? subscription;
  final List _idCardType = [
    ThaiIDType.cid,
    ThaiIDType.photo,
    ThaiIDType.nameTH,
    ThaiIDType.nameEN,
    ThaiIDType.gender,
    ThaiIDType.birthdate,
    ThaiIDType.address,
    ThaiIDType.issueDate,
    ThaiIDType.expireDate,
  ];
  List<String> selectedTypes = [];

  @override
  void initState() {
    super.initState();
    // 监听设备连接事件
    ThaiIdcardReaderFlutter.deviceHandlerStream.listen(_onUSB);
  }

  // 处理设备连接事件
  void _onUSB(usbEvent) {
    try {
      if (usbEvent.hasPermission) {
        // 如果设备已连接并获得权限,监听卡片插入事件
        subscription = ThaiIdcardReaderFlutter.cardHandlerStream.listen(_onData);
      } else {
        // 如果设备断开连接,取消监听卡片插入事件
        if (subscription == null) {
          subscription?.cancel();
          subscription = null;
        }
        _clear();
      }
      setState(() {
        _device = usbEvent;
      });
    } catch (e) {
      setState(() {
        _error = "_onUSB " + e.toString();
      });
    }
  }

  // 处理卡片插入事件
  void _onData(readerEvent) {
    try {
      setState(() {
        _card = readerEvent;
      });
      if (readerEvent.isReady) {
        // 读取卡片数据
        readCard(only: selectedTypes);
      } else {
        _clear();
      }
    } catch (e) {
      setState(() {
        _error = "_onData " + e.toString();
      });
    }
  }

  // 读取卡片数据
  readCard({List<String> only = const []}) async {
    try {
      var response = await ThaiIdcardReaderFlutter.read(only: only);
      setState(() {
        _data = response;
      });
    } catch (e) {
      setState(() {
        _error = 'ERR readCard $e';
      });
    }
  }

  // 格式化日期
  formattedDate(dt) {
    try {
      DateTime dateTime = DateTime.parse(dt);
      String formattedDate = DateFormat.yMMMMd('th_TH').format(dateTime);
      return formattedDate;
    } catch (e) {
      return dt.split('').toString() + e.toString();
    }
  }

  // 清除数据
  _clear() {
    setState(() {
      _data = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Thai ID Card Reader'),
        ),
        body: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              if (_device != null)
                UsbDeviceCard(
                  device: _device,
                ),
              if (_card != null) Text(_card.toString()),
              if (_device == null || !_device!.isAttached) ...[
                const EmptyHeader(
                  text: '请插入读卡器',
                ),
              ],
              if (_error != null) Text(_error.toString()),
              if (_data == null && (_device != null && _device!.hasPermission)) ...[
                const EmptyHeader(
                  icon: Icons.credit_card,
                  text: '请插入身份证',
                ),
                SizedBox(
                  height: 200,
                  child: Wrap(children: [
                    Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Checkbox(
                            value: selectedTypes.isEmpty,
                            onChanged: (val) {
                              setState(() {
                                if (selectedTypes.isNotEmpty) {
                                  selectedTypes = [];
                                }
                              });
                            }),
                        const Text('读取全部'),
                      ],
                    ),
                    for (var ea in _idCardType)
                      Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          Checkbox(
                              value: selectedTypes.contains(ea),
                              onChanged: (val) {
                                print(ea);
                                setState(() {
                                  if (selectedTypes.contains(ea)) {
                                    selectedTypes.remove(ea);
                                  } else {
                                    selectedTypes.add(ea);
                                  }
                                });
                              }),
                          Text('$ea'),
                        ],
                      ),
                  ]),
                ),
              ],
              if (_data != null) ...[
                const Padding(padding: EdgeInsets.all(8.0)),
                if (_data!.photo.isNotEmpty)
                  Center(
                    child: Image.memory(
                      Uint8List.fromList(_data!.photo),
                    ),
                  ),
                if (_data!.cid != null) DisplayInfo(title: '身份证号码', value: _data!.cid!),
                if (_data!.firstnameTH != null)
                  DisplayInfo(
                      title: '姓名(泰语)',
                      value: '${_data!.titleTH} ${_data!.firstnameTH} ${_data?.lastnameTH!}'),
                if (_data!.firstnameEN != null)
                  DisplayInfo(
                      title: '姓名(英语)',
                      value: '${_data!.titleEN} ${_data!.firstnameEN} ${_data!.lastnameEN}'),
                if (_data!.gender != null)
                  DisplayInfo(title: '性别', value: '(${_data!.gender}) ${_data!.gender == 1 ? '男' : '女'}'),
                if (_data!.birthdate != null)
                  DisplayInfo(
                      title: '出生日期',
                      value: '${_data!.birthdate.toString()}\n${formattedDate(_data!.birthdate)}'),
                if (_data!.address != null) DisplayInfo(title: '地址', value: _data!.address!),
                if (_data!.issueDate != null)
                  DisplayInfo(
                      title: '发证日期', value: '${_data!.issueDate.toString()}\n${formattedDate(_data!.issueDate)}'),
                if (_data!.expireDate != null)
                  DisplayInfo(
                      title: '有效期至',
                      value: '${_data!.expireDate.toString()}\n${formattedDate(_data!.expireDate)}'),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

// 显示空状态的组件
class EmptyHeader extends StatelessWidget {
  final IconData? icon;
  final String? text;
  const EmptyHeader({
    this.icon,
    this.text,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
        child: SizedBox(
            height: 300,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(
                  icon ?? Icons.usb,
                  size: 60,
                ),
                Center(
                    child: Text(
                  text ?? 'Empty',
                  textAlign: TextAlign.center,
                  style: const TextStyle(
                    fontSize: 32,
                    fontWeight: FontWeight.bold,
                  ),
                )),
              ],
            )));
  }
}

// 显示USB设备信息的组件
class UsbDeviceCard extends StatelessWidget {
  final dynamic device;
  const UsbDeviceCard({
    Key? key,
    this.device,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Opacity(
      opacity: device.isAttached ? 1.0 : 0.5,
      child: Card(
        child: ListTile(
          leading: const Icon(
            Icons.usb,
            size: 32,
          ),
          title: Text('${device!.manufacturerName} ${device!.productName}'),
          subtitle: Text(device!.identifier ?? ''),
          trailing: Container(
            padding: const EdgeInsets.all(8),
            color: device!.hasPermission ? Colors.green : Colors.grey,
            child: Text(device!.hasPermission ? 'Listening' : (device!.isAttached ? 'Connected' : 'Disconnected'),
                style: const TextStyle(
                  fontWeight: FontWeight.bold,
                )),
          ),
        ),
      ),
    );
  }
}

// 显示信息的组件
class DisplayInfo extends StatelessWidget {
  const DisplayInfo({
    Key? key,
    required this.title,
    required this.value,
  }) : super(key: key);

  final String title;
  final String value;

  @override
  Widget build(BuildContext context) {
    TextStyle sTitle = const TextStyle(fontSize: 24, fontWeight: FontWeight.bold);
    TextStyle sVal = const TextStyle(fontSize: 28);

    // 复制文本到剪贴板
    _copyFn(value) {
      Clipboard.setData(ClipboardData(text: value)).then((_) {
        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("已复制")));
      });
    }

    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          Row(
            children: [
              Text(
                '$title : ',
                style: sTitle,
              ),
            ],
          ),
          Stack(
            alignment: Alignment.centerRight,
            children: [
              Row(
                children: [
                  Flexible(
                    child: Text(
                      value,
                      style: sVal,
                    ),
                  ),
                ],
              ),
              GestureDetector(
                onTap: () => _copyFn(value),
                child: const Icon(Icons.copy),
              )
            ],
          ),
          const Divider(
            color: Colors.black,
          ),
        ],
      ),
    );
  }
}

可选参数

你可以通过传递不同的参数来读取特定的数据:

// 读取所有数据
await ThaiIdcardReaderFlutter.read();

// 仅读取特定数据
await ThaiIdcardReaderFlutter.read(only: [
    ThaiIDType.cid,
    ThaiIDType.photo,
    ThaiIDType.nameTH,
    ThaiIDType.nameEN,
    ThaiIDType.gender,
    ThaiIDType.birthdate,
    ThaiIDType.address,
    ThaiIDType.issueDate,
    ThaiIDType.expireDate,
]);

更多关于Flutter泰国身份证读取插件thai_idcard_reader_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter泰国身份证读取插件thai_idcard_reader_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用thai_idcard_reader_flutter插件来读取泰国身份证信息的示例代码。这个插件主要用于扫描和解析泰国身份证信息。

首先,确保你已经在pubspec.yaml文件中添加了thai_idcard_reader_flutter依赖:

dependencies:
  flutter:
    sdk: flutter
  thai_idcard_reader_flutter: ^最新版本号  # 请替换为实际的最新版本号

然后,运行flutter pub get来获取依赖。

接下来,在你的Flutter项目中,你可以使用以下代码来集成并使用该插件:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Thai ID Card Reader Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: IDCardReaderScreen(),
    );
  }
}

class IDCardReaderScreen extends StatefulWidget {
  @override
  _IDCardReaderScreenState createState() => _IDCardReaderScreenState();
}

class _IDCardReaderScreenState extends State<IDCardReaderScreen> {
  ThaiIDCardData? idCardData;

  Future<void> _scanIDCard() async {
    try {
      // 调用插件方法来扫描身份证
      idCardData = await ThaiIDCardReaderFlutter.scanIDCard();
      
      // 更新UI
      setState(() {});
    } catch (e) {
      // 处理错误
      print('Error scanning ID card: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Thai ID Card Reader Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: _scanIDCard,
              child: Text('Scan ID Card'),
            ),
            SizedBox(height: 20),
            if (idCardData != null)
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Card(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text('Full Name: ${idCardData!.fullName}'),
                      Text('Birth Date: ${idCardData!.birthDate}'),
                      Text('Address: ${idCardData!.address}'),
                      Text('ID Number: ${idCardData!.idNumber}'),
                      // 根据需要添加更多字段
                    ],
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

class ThaiIDCardData {
  final String fullName;
  final String birthDate;
  final String address;
  final String idNumber;
  // 根据需要添加更多字段

  ThaiIDCardData({
    required this.fullName,
    required this.birthDate,
    required this.address,
    required this.idNumber,
    // 根据需要添加更多字段的初始化
  });
}

// 注意:ThaiIDCardData 类是一个示例,实际使用时需要根据插件返回的字段来定义

注意事项:

  1. 权限处理:在实际应用中,你可能需要处理相机权限和存储权限。你可以在AndroidManifest.xmlInfo.plist文件中声明这些权限,并在运行时请求它们。

  2. 错误处理:示例代码中已经包含了基本的错误处理,但在实际项目中,你可能需要更详细的错误处理逻辑。

  3. 插件更新:确保你使用的是插件的最新版本,以获取最新的功能和修复。

  4. UI调整:根据实际需求调整UI布局和样式。

  5. 字段映射ThaiIDCardData类中的字段需要根据thai_idcard_reader_flutter插件实际返回的字段进行映射。请参考插件的官方文档以获取详细的字段信息。

这个示例代码展示了如何在Flutter应用中集成并使用thai_idcard_reader_flutter插件来读取泰国身份证信息。希望这对你有所帮助!

回到顶部