Flutter智能卡读写插件flutter_pcsc的使用
Flutter智能卡读写插件flutter_pcsc的使用
flutter_pcsc
是一个用于在 Windows、macOS 和 Linux 上使用 PCSC 智能卡读卡器的 Flutter 插件。本文将介绍如何使用这个插件,并提供一个完整的示例 demo。
使用说明
前提条件
- 一个 PCSC 智能卡读卡器。
- 在 Linux 系统上,需要安装
pcscd
和libpcsclite1
。
示例代码
以下是一个简单的示例,展示了如何使用 flutter_pcsc
插件与智能卡进行通信:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_pcsc/flutter_pcsc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Plugin example app'),
),
body: SmartCardReader(),
),
);
}
}
class SmartCardReader extends StatefulWidget {
[@override](/user/override)
_SmartCardReaderState createState() => _SmartCardReaderState();
}
class _SmartCardReaderState extends State<SmartCardReader> {
List<String> messages = [];
[@override](/user/override)
void initState() {
super.initState();
readCard();
}
Future<void> readCard() async {
int ctx;
CardStruct card;
try {
ctx = await Pcsc.establishContext(PcscSCope.user);
List<String> readers = await Pcsc.listReaders(ctx);
if (readers.isEmpty) {
setState(() {
messages.add('Could not detect any reader');
});
} else {
String reader = readers[0];
setState(() {
messages.add('Using reader: $reader');
});
card = await Pcsc.cardConnect(ctx, reader, PcscShare.shared, PcscProtocol.any);
var response = await Pcsc.transmit(card, [0xFF, 0xCA, 0x00, 0x00, 0x00]);
setState(() {
messages.add('Response: ${response.toString()}');
});
await Pcsc.cardDisconnect(card.hCard, PcscDisposition.resetCard);
}
} catch (e) {
setState(() {
messages.add('Error: $e');
});
} finally {
await Pcsc.releaseContext(ctx);
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(messages[index]),
);
},
);
}
}
macOS 应用注意事项
对于 macOS 应用程序,为了能够使用智能卡,需要在 DebugProfile.entitlements
和 Release.entitlements
文件中设置以下权限:
<key>com.apple.security.smartcard</key>
<true/>
如果不正确设置,上下文将无法建立。
完整示例 Demo
以下是一个更完整的示例,包含错误处理和用户界面更新:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_pcsc/flutter_pcsc.dart';
enum MessageType { info, error }
class Message {
final String content;
final MessageType type;
Message(this.type, this.content);
static info(String content) => Message(MessageType.info, content);
static error(String content) => Message(MessageType.error, content);
}
class MyAppBody extends StatefulWidget {
const MyAppBody({Key? key}) : super(key: key);
[@override](/user/override)
_MyAppBodyState createState() => _MyAppBodyState();
}
class _MyAppBodyState extends State<MyAppBody> {
static const List<int> getCardSerialNumberCommand = [0xFF, 0xCA, 0x00, 0x00, 0x00];
final ScrollController _scrollController = ScrollController();
final List<Message> messages = [];
[@override](/user/override)
void initState() {
super.initState();
getCardSerialNumber();
}
Future<void> getCardSerialNumber() async {
int ctx;
CardStruct? card;
try {
ctx = await Pcsc.establishContext(PcscSCope.user);
List<String> readers = await Pcsc.listReaders(ctx);
if (readers.isEmpty) {
messages.add(Message.error('Could not detect any reader'));
} else {
String reader = readers[0];
setState(() {
messages.add(Message.info('Using reader: $reader'));
});
card = await Pcsc.cardConnect(ctx, reader, PcscShare.shared, PcscProtocol.any);
var response = await Pcsc.transmit(card, getCardSerialNumberCommand);
var sw = response.sublist(response.length - 2);
var sn = response.sublist(0, response.length - 2);
if (sw[0] != 0x90 || sw[1] != 0x00) {
setState(() {
messages.add(Message.error('Card returned an error: ${hexDump(sw)}'));
});
} else {
setState(() {
messages.add(Message.info('Card Serial Number is: ${hexDump(sn)}'));
messages.add(Message.info('Done'));
});
}
}
} catch (e) {
messages.add(Message.error(e.toString()));
} finally {
if (card != null) {
try {
await Pcsc.cardDisconnect(card.hCard, PcscDisposition.resetCard);
} catch (e) {
messages.add(Message.error(e.toString()));
}
}
try {
await Pcsc.releaseContext(ctx);
} catch (e) {
messages.add(Message.error(e.toString()));
}
}
}
static String hexDump(List<int> csn) {
return csn.map((i) => i.toRadixString(16).padLeft(2, '0').toUpperCase()).join(' ');
}
void _scrollToBottom() {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
[@override](/user/override)
Widget build(BuildContext context) {
TextStyle errorStyle = const TextStyle(color: Colors.red);
WidgetsBinding.instance?.addPostFrameCallback((_) => _scrollToBottom());
return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Expanded(
child: Column(children: [
Expanded(
child: ListView(
controller: _scrollController,
children: messages.map((e) => Text(e.content, style: e.type == MessageType.error ? errorStyle : null)).toList(),
),
),
Container(
margin: const EdgeInsets.all(10),
child: ElevatedButton(
onPressed: () async {
messages.clear();
await getCardSerialNumber();
},
child: const Text("Try again"),
),
)
]),
)
]);
}
}
void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: MyAppBody(),
),
));
}
更多关于Flutter智能卡读写插件flutter_pcsc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter智能卡读写插件flutter_pcsc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用flutter_pcsc
插件进行智能卡读写的示例代码。这个插件允许你与智能卡(如RFID卡、SIM卡等)进行通信。在使用这个插件之前,请确保你的开发环境已经配置好Flutter,并且你已经在pubspec.yaml
文件中添加了flutter_pcsc
依赖。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加flutter_pcsc
依赖:
dependencies:
flutter:
sdk: flutter
flutter_pcsc: ^x.y.z # 替换为最新版本号
然后运行flutter pub get
来获取依赖。
2. 配置权限(Android)
在android/app/src/main/AndroidManifest.xml
中添加必要的权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<uses-permission android:name="android.permission.NFC" />
<!-- 其他必要的权限 -->
<application
... >
...
</application>
</manifest>
注意:NFC
权限可能不是必需的,具体取决于你的智能卡读写需求。
3. 使用flutter_pcsc
插件
下面是一个简单的Flutter应用示例,展示了如何使用flutter_pcsc
插件来列出可用的智能卡读取器并与智能卡通信。
import 'package:flutter/material.dart';
import 'package:flutter_pcsc/flutter_pcsc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<SCardReader> _readers = [];
String _cardData = '';
@override
void initState() {
super.initState();
_listReaders();
}
Future<void> _listReaders() async {
try {
var readers = await SCard.listReaders();
setState(() {
_readers = readers;
});
if (_readers.isNotEmpty) {
// 连接到第一个可用的读取器
var reader = _readers.first;
var context = await SCard.establishContext();
var handle = await context.connect(reader.name);
// 等待卡片插入
var atr = await handle.waitForCard();
print('ATR: $atr');
// 进行APDU命令(示例:选择MF)
var commandApdu = Uint8List.fromList([0x00, 0xA4, 0x04, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x21, 0x00]);
var responseApdu = await handle.transmit(commandApdu);
print('Response APDU: ${responseApdu.map((e) => e.toRadixString(16).padLeft(2, '0')).join(' ')}');
// 断开连接并释放上下文
await handle.disconnect();
await context.release();
}
} catch (e) {
print('Error: $e');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter PC/SC Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text('Available Readers:'),
SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: _readers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_readers[index].name),
);
},
),
),
SizedBox(height: 16),
Text('Card Data: $_cardData'),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _listReaders,
tooltip: 'List Readers',
child: Icon(Icons.refresh),
),
),
);
}
}
说明
- 初始化:在
initState
中调用_listReaders
函数来列出可用的智能卡读取器。 - 列出读取器:使用
SCard.listReaders()
列出所有可用的智能卡读取器。 - 连接到读取器:使用
SCard.establishContext()
建立上下文,并使用context.connect(reader.name)
连接到第一个可用的读取器。 - 等待卡片插入:使用
handle.waitForCard()
等待卡片插入,并获取卡片的ATR(Answer To Reset)。 - APDU命令:发送APDU命令与卡片通信。这里只是一个示例命令,用于选择MF(主文件)。
- 断开连接:使用
handle.disconnect()
断开与读取器的连接,并使用context.release()
释放上下文。
注意:上述代码只是一个基本示例,实际应用中可能需要处理更多的错误情况和异常,并根据具体的智能卡协议发送适当的APDU命令。