nfc_in_flutter
NFC in Flutter 是一个用于在 Flutter 中读写 NFC 标签的插件。它通过简单的流接口同时支持 Android 和 iOS。
⚠️ 目前仅支持 NDEF 格式的标签。
用法
读取 NFC 标签
// NFC.readNDEF returns a stream of NDEFMessage
Stream<NDEFMessage> stream = NFC.readNDEF();
stream.listen((NDEFMessage message) {
print("records: ${message.records.length}");
});
读取一个 NFC 标签
NDEFMessage message = await NFC.readNDEF(once: true).first;
print("payload: ${message.payload}");
// once: true` only scans one tag!
写入标签
您可以通过 NDEFMessage 的 .tag 属性访问消息的 NFC 标签。该标签有一个 .write 方法,允许您将 NDEF 消息写入标签。
请注意,在调用 .write 方法时,读取流必须保持打开状态。这意味着 .readNDEF() 中的 once 参数不能使用。
Stream<NDEFMessage> stream = NFC.readNDEF();
stream.listen((NDEFMessage message) {
NDEFMessage newMessage = NDEFMessage.withRecords(
NDEFRecord.mime("text/plain", "hello world")
);
message.tag.write(newMessage);
});
您还可以使用 NFC.writeNDEF(NDEFMessage) 方法,该方法封装了上述代码,并支持 once 参数。
NDEFMessage newMessage = NDEFMessage.withRecords(
NDEFRecord.mime("text/plain", "hello world")
);
Stream<NDEFTag> stream = NFC.writeNDEF(newMessage);
stream.listen((NDEFTag tag) {
print("wrote to tag");
});
如果您只想写入一个标签,可以将 once 参数设置为 true。
NDEFMessage newMessage = NDEFMessage.withRecords(
NDEFRecord.mime("text/plain", "hello world")
);
Stream<NDEFTag> stream = NFC.writeNDEF(newMessage, once: true);
stream.listen((NDEFTag tag) {
print("only wrote to one tag!");
});
如果您更喜欢使用基于 Future 的 API,则可以 await 返回流的 .first 方法。
NDEFMessage newMessage = NDEFMessage.withRecords(
NDEFRecord.type("text/plain", "hello world")
);
await NFC.writeNDEF(newMessage, once: true).first;
示例
import 'package:nfc_in_flutter/nfc_in_flutter.dart';
class NFCReader extends StatefulWidget {
@override
_NFCReaderState createState() => _NFCReaderState();
}
class _NFCReaderState extends State {
bool _supportsNFC = false;
bool _reading = false;
StreamSubscription<NDEFMessage> _stream;
@override
void initState() {
super.initState();
// Check if the device supports NFC reading
NFC.isNDEFSupported
.then((bool isSupported) {
setState(() {
_supportsNFC = isSupported;
});
});
}
@override
Widget build(BuildContext context) {
if (!_supportsNFC) {
return RaisedButton(
child: const Text("You device does not support NFC"),
onPressed: null,
);
}
return RaisedButton(
child: Text(_reading ? "Stop reading" : "Start reading"),
onPressed: () {
if (_reading) {
_stream?.cancel();
setState(() {
_reading = false;
});
} else {
setState(() {
_reading = true;
// Start reading using NFC.readNDEF()
_stream = NFC.readNDEF(
once: true,
throwOnUserCancel: false,
).listen((NDEFMessage message) {
print("read NDEF message: ${message.payload}"),
}, onError: (e) {
// Check error handling guide below
});
});
}
}
);
}
}
完整的示例请参见 example 目录
安装
将 nfc_in_flutter 添加到您的 pubspec.yaml 文件中
dependencies:
nfc_in_flutter: 2.0.5
iOS
在 iOS 上,您必须开启“近场通信”功能,添加 NFC 使用说明和 NFC 权限。
开启近场通信标签读取
在 Xcode 中打开您的 iOS 项目,找到项目的 Target,然后导航到 Capabilities。向下滚动找到“Near Field Communication Tag Reading”并开启它。
开启“近场通信标签读取”
- 将 NFC 标签读取功能添加到 App ID。
- 将 Near Field Communication Tag Reader Session Formats Entitlement 添加到 entitlements 文件。
来自 developer.apple.com: Building an NFC Tag-Reader app
NFC 使用说明
打开您的 ios/Runner/Info.plist 文件,并添加一个新的 NFCReaderUsageDescription 键。它的值应该是您计划使用 NFC 的说明。
<key>NFCReaderUsageDescription</key>
<string>...</string>
Android
在您的 AndroidManifest.xml 文件中添加以下内容
<uses-permission android:name="android.permission.NFC" />
如果您的应用需要 NFC,您可以添加以下内容,仅允许在支持 NFC 的设备上下载
<uses-feature android:name="android.hardware.nfc" android:required="true" />
“什么是 NDEF?”
如果您是 NFC 新手,可能会期望有很多 readNFC() 调用,但实际上看到的是 readNDEF() 和 NDEFMessage。NDEF 只是标签可以编码的一种格式标准。除了 NDEF 之外,还有其他编码方式,但 NDEF 是最常见的。目前 Flutter 中的 NFC 只支持 NDEF 格式的标签。
主机卡模拟
Flutter 中的 NFC 支持从模拟的主机卡*读取。
要从模拟的主机卡读取,您需要做几件事。
- 调用
readNDEF(),并将readerMode参数设置为NFCDispatchReaderMode的实例。 - 在您的
AndroidManifest.xmlactivity 中插入以下<intent-filter />
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
- 在 iOS 上未经过充分测试
⚠️ 多个读取模式
如果您以 NFCDispatchReaderMode 作为读取模式启动一个 readNDEF() 流,而此时另一个流正在以 NFCNormalReaderMode 模式运行,则会抛出 NFCMultipleReaderModesException。
平台差异
当您在 iOS 上调用 readNDEF() 时,Core NFC(允许 NFC 读取的 iOS 框架)会打开一个小窗口。在 Android 上,它只是在后台开始监听 NFC 标签读取。
来自 developer.apple.com: Near Field Communication 的图片
⚠️ 这也会冻结 Flutter。如果您能修复此问题,请发送一个 Pull Request。
错误处理
错误也同样适用于 NFC in Flutter (哈哈,懂了吗)。NFC.readNDEF() 返回的流可以发送 7 种不同的异常,更糟糕的是:它们在不同平台上是不同的!
请参见 example 目录 中的完整示例,了解如何检查错误。
两个平台的异常
NDEFReadingUnsupportedException
在启动读取会话但不支持时抛出。
iOS
NFCUserCanceledSessionException
当用户点击 Core NFC 弹窗中的 Cancel/Done 时抛出。如果您不需要知道用户是否取消了会话,可以将 throwOnUserCancel 参数设置为 false 来开始读取,例如:readNDEF(throwOnUserCancel: false)
NFCSessionTimeoutException
Core NFC 将 NFC 读取会话限制在 60 秒。当会话活动时间达到 60 秒时,会抛出 NFCSessionTimeoutException。
NFCSessionTerminatedUnexpectedlyException
在读取会话意外终止时抛出。
NFCSystemIsBusyException
当读取会话因系统繁忙而失败时抛出。
Android
NFCIOException
在发生 I/O 异常时抛出。例如,如果在读取过程中标签丢失或无法连接到标签时会发生这种情况。
NDEFBadFormatException
当标签被期望为 NDEF 格式但格式不正确时抛出。

