Updated June 2026. Tested on Flutter 3.x and Dart 3. Part of the Techalyst Flutter series.
NFC is the tap-to-read tech behind contactless payments, transit cards, and the little stickers that open a URL when you touch your phone to them. If your app needs to read a product tag, a membership card, or any small chunk of data off a tag, the nfc_manager package handles it in Flutter. The flow is short once you know the shape of it, but there are real platform caveats worth knowing before you start.
Setup and platform notes
Add nfc_manager to pubspec.yaml. NFC is mobile only, there is no web or desktop support, and you must test on a real device because simulators have no NFC radio.
Each platform needs a little configuration. iOS requires the Near Field Communication entitlement turned on for your app and an NFCReaderUsageDescription string in Info.plist. Android needs the NFC permission in the manifest. iOS is also stricter at runtime: it shows a system scanning sheet you cannot fully restyle, while Android can scan more quietly.
Check availability first
Not every phone has NFC, and the user may have it switched off, so check before you try to scan:
final isAvailable = await NfcManager.instance.isAvailable();
if (!isAvailable) {
// tell the user NFC is unavailable or disabled
return;
}
Starting a scan session
You start a session and hand it a callback. When the user taps a tag, your onDiscovered runs with an NfcTag. You then stop the session, always, because leaving it open keeps the reader active:
void startScan() {
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
final ndef = Ndef.from(tag);
if (ndef == null) {
await NfcManager.instance.stopSession(errorMessage: 'This tag is not supported.');
return;
}
final message = ndef.cachedMessage;
// read records from message here
await NfcManager.instance.stopSession();
},
);
}
Ndef.from(tag) returns null when the tag is not NDEF formatted, which is the common, well-known format for text and URLs. Handle that case rather than assuming every tag carries readable data.
Reading the records
An NDEF message is a list of records, and each record's payload is raw bytes. For a text record, the payload has a small header before the actual text, so you skip a few bytes and decode the rest:
final records = ndef.cachedMessage?.records ?? [];
for (final record in records) {
final payload = record.payload;
// text records: first byte is a status byte, then a language code, then the text
final languageLength = payload.first & 0x3F;
final text = String.fromCharCodes(payload.sublist(1 + languageLength));
print('Tag text: $text');
}
URI records are encoded similarly with a prefix byte that maps to schemes like https://. For anything beyond plain text, it is worth leaning on a helper that decodes NDEF records for you rather than parsing bytes by hand every time.
Writing a tag, briefly
The same package writes tags too. If the tag is writable, you build an NdefMessage and call ndef.write, which is how you would program a sticker to open your app or a link.
Wrapping up
Reading NFC in Flutter with nfc_manager is a short loop: check isAvailable, start a session with an onDiscovered callback, pull the NDEF message off the tag, decode its records, and always stop the session afterwards. Keep the platform realities in mind, it is mobile only, iOS is stricter and shows its own sheet, and you have to test on real hardware. Handle the not-supported and not-NDEF cases up front and tap-to-read becomes a genuinely nice feature to ship.
All comments ()
No comments yet
Be the first to leave a comment on this post.