Files
idcard/example/lib/main.dart
2026-03-31 08:51:18 +08:00

367 lines
12 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:idcard/idcard.dart';
import 'package:idcard/idcard_platform_interface.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
String _status = '未连接';
IdCardInfo? _cardInfo;
bool _isReading = false;
bool _isLoading = false;
bool _isConnected = false;
String _statusMessage = '未连接';
final _idcardPlugin = Idcard();
final String _portType = "USB";
final String _portPara = "0400C35A";
final String _portExtend = "MI";
@override
void initState() {
super.initState();
initPlatformState();
}
/// 初始化平台状态
Future<void> initPlatformState() async {
String platformVersion;
try {
platformVersion = await _idcardPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
/// 连接设备
Future<void> _connectDevice() async {
setState(() {
_isLoading = true;
_statusMessage = '正在连接设备...';
_status = '正在连接设备...';
});
try {
// 首先获取USB权限
setState(() {
_statusMessage = '正在请求USB权限...';
_status = '正在请求USB权限...';
});
// int vid = int.parse(portPara.substring(0, 4), radix: 16);
// int pid = int.parse(portPara.substring(4, 8), radix: 16);
final permissionResult = await _idcardPlugin.getUsbPermission(vid: int.parse(_portPara.substring(0, 4), radix: 16), pid: int.parse(_portPara.substring(4, 8), radix: 16));
if (permissionResult != 0) {
throw Exception('获取USB权限失败: $permissionResult');
}
// 打开设备
setState(() {
_statusMessage = '正在打开设备...';
_status = '正在打开设备...';
});
// "USB", "0400C35A", "MI";
final openResult = await _idcardPlugin.openDevice(portType: _portType, portPara: _portPara, extendPara: _portExtend);
print("设备句柄:$openResult");
if (openResult <= 0) {
throw Exception('打开设备失败: $openResult');
}
setState(() {
_isConnected = true;
_statusMessage = '设备连接成功';
_status = '设备连接成功';
});
} on PlatformException catch (e) {
String errorMessage = '连接失败: ${e.message}';
// 针对不同错误提供用户友好的提示
if (e.code == 'PERMISSION_DENIED') {
errorMessage = 'USB权限被拒绝。\n\n解决方法:\n1. 请在弹出的权限对话框中点击"允许"\n2. 或者在系统设置中手动授权USB权限\n3. 确保设备已正确连接';
} else if (e.code == 'DEVICE_NOT_FOUND') {
errorMessage = '未找到身份证读卡器设备。\n\n请检查:\n1. 设备是否已连接\n2. 设备驱动是否正常\n3. VID/PID是否正确';
}
setState(() {
_statusMessage = errorMessage;
_status = errorMessage;
_isConnected = false;
});
} catch (e) {
setState(() {
_statusMessage = '连接失败: $e';
_status = '连接失败: $e';
_isConnected = false;
});
} finally {
setState(() {
_isLoading = false;
});
}
}
/// 断开设备连接
Future<void> _disconnectDevice() async {
try {
await _idcardPlugin.closeDevice();
setState(() {
_status = '设备已断开';
_statusMessage = '设备已断开';
_isConnected = false;
_cardInfo = null;
});
} catch (e) {
setState(() {
_status = '断开连接失败:$e';
_statusMessage = '断开连接失败:$e';
});
}
}
/// 读取身份证
Future<void> _readCard() async {
if (_isReading) return;
setState(() {
_isReading = true;
_status = '请将身份证放在读卡器上...';
_cardInfo = null;
});
try {
// 使用完整的读卡流程
IdCardInfo cardInfo = await _idcardPlugin.readCardComplete();
print(cardInfo);
setState(() {
_cardInfo = cardInfo;
_status = '读卡成功';
_isReading = false;
});
} catch (e) {
setState(() {
_status = '读卡失败:$e';
_isReading = false;
});
}
}
/// 构建身份证信息显示卡片
Widget _buildCardInfoWidget() {
if (_cardInfo == null) {
return const SizedBox.shrink();
}
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'身份证信息',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
_buildInfoRow('姓名', _cardInfo!.name),
_buildInfoRow('性别', _cardInfo!.gender),
_buildInfoRow('民族', _cardInfo!.nation),
_buildInfoRow('出生日期', _cardInfo!.birthDate),
_buildInfoRow('身份证号', _cardInfo!.idNumber),
_buildInfoRow('地址', _cardInfo!.address),
_buildInfoRow('签发机关', _cardInfo!.signOrgan),
_buildInfoRow('有效期', _cardInfo!.validTerm),
if (_cardInfo!.photo != null) ...[
const SizedBox(height: 16),
const Text('照片:'),
const SizedBox(height: 8),
Container(
width: 120,
height: 160,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Image.memory(
Uint8List.fromList(_cardInfo!.photo!),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return const Center(
child: Text('照片加载失败'),
);
},
),
),
],
],
),
),
);
}
/// 构建信息行
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80,
child: Text(
'$label',
style: const TextStyle(fontWeight: FontWeight.w500),
),
),
Expanded(
child: Text(value),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '身份证读卡器示例',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: Scaffold(
appBar: AppBar(
title: const Text('身份证读卡器示例'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
child: Column(
children: [
// 平台信息
Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'平台信息',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text('运行平台:$_platformVersion'),
const SizedBox(height: 8),
Text('设备状态:$_status'),
],
),
),
),
// 操作按钮
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: (_isLoading || _isConnected) ? null : _connectDevice,
child: _isLoading
? const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
SizedBox(width: 8),
Text('连接中...'),
],
)
: const Text('连接设备'),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: (_isLoading || !_isConnected) ? null : _disconnectDevice,
child: const Text('断开连接'),
),
),
],
),
),
const SizedBox(height: 16),
// 读卡按钮
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: (_isReading || !_isConnected || _isLoading) ? null : _readCard,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: _isReading
? const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
SizedBox(width: 8),
Text('正在读卡...'),
],
)
: const Text(
'读取身份证',
style: TextStyle(fontSize: 16),
),
),
),
),
// 身份证信息显示
_buildCardInfoWidget(),
],
),
),
),
);
}
}