nfc_util 2.0.0
nfc_util: ^2.0.0 copied to clipboard
A Flutter plugin providing access to NFC features on Android and iOS.
๐ก NFC Util #
A Flutter plugin for reading and writing NFC tags on Android and iOS.
It keeps the familiar nfc_manager v3 API โ migrating is usually just a change of import โ
but ships its own Android and iOS implementations so it can be updated independently and
keep pace with current Flutter, Android and iOS releases.
| ๐ค Android | minSdk 24 ยท full tag I/O (NDEF, transceive, MIFARE Classic / Ultralight, NDEF formatting) |
| ๐ iOS | 15.6+ ยท NDEF, FeliCa, ISO7816, ISO15693, MIFARE |
| ๐ฆ Packaging | CocoaPods and Swift Package Manager |
| ๐ Privacy | PrivacyInfo.xcprivacy bundled by both packaging paths |
๐ Install #
dependencies:
nfc_util: ^2.0.0
Requires Flutter 3.44.0 or newer. Earlier releases do not stage the
FlutterFramework Swift package that the plugin's Package.swift depends on.
โ๏ธ Setup #
๐ค Android #
Add the NFC permission to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.NFC" />
Nothing else โ the plugin ships its own manifest and Gradle configuration.
๐ iOS #
1. Entitlement. Add Near Field Communication Tag Reader Session Formats
to Runner.entitlements:
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>NDEF</string>
<string>TAG</string>
</array>
2. Usage description. Add NFCReaderUsageDescription
to Info.plist โ the text shown on the scan sheet.
3. Deployment target. Set IPHONEOS_DEPLOYMENT_TARGET to 15.6 or higher in
ios/Runner.xcodeproj/project.pbxproj. A lower target fails at install time under
both SwiftPM (requires minimum platform version 15.6) and CocoaPods.
4. Tag-specific Info.plist keys, if you use those technologies:
| Key | Needed for |
|---|---|
com.apple.developer.nfc.readersession.felica.systemcodes |
polling iso18092 (FeliCa) |
com.apple.developer.nfc.readersession.iso7816.select-identifiers |
before an ISO7816 tag reports its applications |
โ ๏ธ The mistake that costs everyone an afternoon #
startSessionpolls every option when you omitpollingOptionsโ includingiso18092. CoreNFC rejects a FeliCa session unlesscom.apple.developer.nfc.readersession.felica.systemcodesis in yourInfo.plist.When that happens the scan sheet never appears and
startSessionstill succeeds, because iOS reports the failure asynchronously. With noonErrorcallback you get total silence and nothing to debug.Fix it either way:
// Ask for only what you need... NfcManager.instance.startSession( pollingOptions: {NfcPollingOption.iso14443}, onDiscovered: (tag) async { /* ... */ }, onError: (error) async => debugPrint('$error'), // ...and always pass this );
๐ Usage #
๐ Session #
// Is NFC present and switched on? Returns false instead of throwing.
final available = await NfcManager.instance.isAvailable();
await NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
// Read and write here โ the session stays open until you stop it.
},
onError: (NfcError error) async {
// iOS only: timeout, user cancel, or a session that could not start.
},
);
await NfcManager.instance.stopSession();
| Method | Behaviour |
|---|---|
isAvailable() |
Returns false rather than throwing when the platform cannot answer |
startSession(...) |
Throws PlatformException if the session could not be started |
stopSession(...) |
Never throws โ safe in finally and dispose |
alertMessage / errorMessage set the text on the iOS scan sheet.
invalidateAfterFirstRead (iOS, default true) stops polling for further tags after
the first one. Either way the session stays open so you can run tag commands inside
onDiscovered; close it yourself with stopSession().
๐ท๏ธ Tag technologies #
Get an instance with the from factory. It returns null when the tag does not
support that technology:
final ndef = Ndef.from(tag);
if (ndef == null) {
print('Tag is not NDEF compatible');
return;
}
final message = await ndef.read(); // null when the tag holds no message
await ndef.write(NdefMessage([NdefRecord.createText('Hello')]));
| Class | ๐ค Android | ๐ iOS |
|---|---|---|
Ndef |
โ | โ |
NfcA NfcB NfcF NfcV |
โ | โ |
IsoDep |
โ | โ |
MifareClassic |
โ | โ |
MifareUltralight |
โ | โ |
NdefFormatable |
โ | โ |
FeliCa |
โ | โ |
Iso7816 |
โ | โ |
Iso15693 |
โ | โ |
MiFare |
โ | โ |
Import the platform classes from:
import 'package:nfc_util/nfc_util.dart'; // NfcManager, NfcTag, Ndef, NdefMessage
import 'package:nfc_util/platform_tags.dart'; // NfcA, MiFare, FeliCa, ...
โ๏ธ Building NDEF records #
NdefMessage([
NdefRecord.createText('Hello World!', languageCode: 'en'),
NdefRecord.createUri(Uri.parse('https://flutter.dev')),
NdefRecord.createMime('text/plain', Uint8List.fromList('Hello'.codeUnits)),
NdefRecord.createExternal('com.example', 'mytype', Uint8List.fromList('data'.codeUnits)),
]);
These factories enforce the NDEF specification and throw ArgumentError on invalid
input. NdefRecord.fromPlatform skips that validation and is what the plugin uses to
decode records read off a tag, which may legitimately hold shapes the creation rules
reject (a chunked record, for instance).
๐งฏ Error handling #
Tag operations throw PlatformException:
| Code | Meaning |
|---|---|
invalid_parameter |
Unknown tag handle, or the tag does not support that technology |
tag_lost |
The tag moved out of range mid-operation |
io_exception |
Communication with the tag failed |
no_result |
The platform returned no value for a call that requires one |
unavailable |
The session could not be started |
On Android the message says whether the failure happened while connecting or while
running the command, e.g. connect: TagLostException: .... Failures are also logged
under the NfcUtilPlugin tag.
On iOS a session can fail after startSession returns โ user cancel, the 60 s
system timeout, or a rejected session. Those arrive on onError, never as a thrown
exception, so always pass the callback.
๐ฆ CocoaPods and Swift Package Manager #
Both are supported and share one copy of the native sources under
ios/nfc_util/Sources/nfc_util/. You do not choose: Flutter uses SwiftPM when it is
enabled (the default) and falls back to CocoaPods otherwise. The privacy manifest ships
either way.
๐ฑ Example #
The example/ app exercises the plugin on a real device: read a tag, write NDEF, lock a
tag, and a Tag I/O button that runs read-only commands against every technology the
tag supports and reports each result with timings.
NFC hardware does not exist in emulators or the iOS Simulator โ the example needs a physical device.