nfc_in_flutter
NFC in Flutter 是一个用于在 Flutter 中读写 NFC 标签的插件。它通过简单的流接口同时支持 Android 和 iOS。
用法
读取 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,可以等待返回流的 .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.3
iOS
在 iOS 上,您必须启用近场通信功能,添加 NFC 使用说明和 NFC 授权。
启用近场通信标签读取
在 Xcode 中打开您的 iOS 项目,找到项目的 Target,然后导航到 Capabilities。向下滚动到 'Near Field Communication Tag Reading' 并将其启用。
'启用近场通信标签读取'
- 将 NFC 标签读取功能添加到 App ID。
- 将 'Near Field Communication Tag Reader Session Formats' 授权添加到 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 是最常见的。目前,NFC in Flutter 只支持 NDEF 格式的标签。
主机卡模拟
NFC in Flutter 支持从模拟主机卡*读取。
要从模拟主机卡读取,您需要做几件事。
- 调用
readNDEF(),并将readerMode参数设置为NFCDispatchReaderMode的实例。 - 将以下
<intent-filter />插入您的AndroidManifest.xmlactivity 中
<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
当用户点击核心 NFC 弹窗的“取消/完成”时抛出。如果您不需要知道用户是否取消了会话,可以将 throwOnUserCancel 参数设置为 false 来开始读取,例如:readNDEF(throwOnUserCancel: false)
NFCSessionTimeoutException
Core NFC 将 NFC 读取会话限制为 60 秒。当会话活动 60 秒后,将抛出 NFCSessionTimeoutException。
NFCSessionTerminatedUnexpectedlyException
当读取会话意外终止时抛出。
NFCSystemIsBusyException
当读取会话由于系统过于繁忙而失败时抛出。
Android
NFCIOException
当发生 I/O 异常时抛出。例如,当标签在读取过程中丢失或无法连接到标签时会发生这种情况。
NDEFBadFormatException
当标签被期望为 NDEF 格式,但格式不正确时抛出。