NFC (Near Field Communication) is one of the most underused capabilities in mobile. Tap-to-share business cards, contactless access control, smart packaging, loyalty, asset tracking — the use cases are everywhere, and the engineering bar is lower than most teams think. This guide covers everything we use to build production NFC apps with Flutter in 2026.
What NFC Apps Are Used For
- Smart business cards — tap to share contact + open profile
- Access control — door entry, hotel keys, gym check-in, event badges
- Loyalty & marketing — tap-to-collect points, smart packaging, in-store activation
- Payments companion — SoftPOS, terminal apps, receipt issuance
- Asset tracking — equipment, samples, chain of custody
- Healthcare — medication adherence, sample IDs, patient bands
- Industrial — inspection logs, maintenance scans, parts identification
- Authentication — second factor on NFC security keys (YubiKey, FIDO)
Platform Capability — iOS vs Android
NFC capability differs sharply between platforms. Plan for both:
| Capability | iOS | Android |
|---|---|---|
| Read NDEF | iOS 11+ | Yes |
| Write NDEF | iOS 13+ | Yes |
| Background reading | iPhone XS+ | Yes (foreground dispatch) |
| MIFARE Classic | No (Apple restricts) | Yes |
| MIFARE DESFire | Yes (with entitlement) | Yes |
| FeliCa | Yes | Yes |
| ISO 15693 | Yes | Yes |
| Host Card Emulation | Apple Pay only | Full HCE supported |
| Peer-to-peer beam | No | Deprecated Android 14+ |
Setting Up NFC in Flutter
The default cross-platform plugin is nfc_manager. Install it:
flutter pub add nfc_manager
iOS Configuration
Add the NFC capability in Xcode, then update Info.plist:
<key>NFCReaderUsageDescription</key>
<string>This app uses NFC to read tags.</string>
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array><string>8005</string></array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array><string>A0000002471001</string></array>
Then add the entitlement com.apple.developer.nfc.readersession.formats.
Android Configuration
Add NFC permissions to AndroidManifest.xml:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
Read an NDEF Tag
import 'package:nfc_manager/nfc_manager.dart';
Future<void> startReading() async {
final isAvailable = await NfcManager.instance.isAvailable();
if (!isAvailable) return;
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
final ndef = Ndef.from(tag);
if (ndef == null) {
await NfcManager.instance.stopSession(errorMessage: 'Not NDEF');
return;
}
final message = ndef.cachedMessage;
for (final record in message?.records ?? []) {
final payload = String.fromCharCodes(record.payload);
print('Record: $payload');
}
await NfcManager.instance.stopSession();
},
);
}
Write an NDEF Tag
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
final ndef = Ndef.from(tag);
if (ndef == null || !ndef.isWritable) {
await NfcManager.instance.stopSession(errorMessage: 'Tag not writable');
return;
}
final message = NdefMessage([
NdefRecord.createUri(Uri.parse('https://devcenter.dev')),
]);
try {
await ndef.write(message);
await NfcManager.instance.stopSession(alertMessage: 'Written!');
} catch (e) {
await NfcManager.instance.stopSession(errorMessage: 'Failed: $e');
}
},
);
Working With MIFARE on Android
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
final mifare = MifareClassic.from(tag);
if (mifare == null) return;
final auth = await mifare.authenticateSectorWithKeyA(
sectorIndex: 1,
key: Uint8List.fromList([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
);
if (!auth) {
await NfcManager.instance.stopSession(errorMessage: 'Auth failed');
return;
}
final block = await mifare.readBlock(blockIndex: 4);
print('Block 4: $block');
await NfcManager.instance.stopSession();
},
);
Host Card Emulation (Android only)
HCE lets your Flutter app emulate an NFC card — useful for transit, loyalty, or custom access systems. Register a service in AndroidManifest.xml:
<service
android:name=".HceService"
android:exported="true"
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
</intent-filter>
<meta-data
android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/hce_aid_list" />
</service>
Then implement a native HostApduService and bridge to Flutter via platform channels.
Common Real-World NFC Patterns
Tap-to-Connect Business Card
- Issue programmable NFC card with NTAG215 chip
- Write a URL pointing to the user's profile
- Recipient taps phone → opens profile in browser or app
- Optional: deep-link into app for "Save Contact"
Access Control with Rotating Keys
- Server generates short-lived token
- App writes token to user's tag or emulates via HCE
- Reader validates token + audits access
- Token rotates every 30 seconds — prevents cloning
Loyalty Tap-to-Collect
- Sticker tag on point-of-sale or product
- Customer taps phone → opens app or web fallback
- Backend credits points + logs event
- Smart packaging variant uses NTAG 424 DNA for tamper detection
Production Pitfalls We Have Solved
- iOS background read confusion: use Background Tag Reading on iPhone XS+, fall back to in-app session elsewhere
- Tag cloning: never trust unsigned NDEF; sign payloads with HMAC or use crypto-capable chips (NTAG 424 DNA, DESFire)
- Scan reliability: provide visual + haptic feedback the moment
onDiscoveredfires - App not installed: write a URL that resolves to a smart page that deep-links if app present, else App Store/Play Store
- Cheap tags fail: use NTAG 213/215/216 minimum — counterfeit clones leak data after writes
- Battery drain: stop session as soon as expected tag is read
Tag Selection Cheat Sheet
- NTAG 213: 144 bytes, cheap, URL or short text
- NTAG 215: 504 bytes, sweet spot for vCards and small payloads
- NTAG 216: 888 bytes, room for richer content
- NTAG 424 DNA: dynamic, cryptographic — anti-clone, anti-counterfeit
- MIFARE Ultralight EV1: cheap, mass produced
- MIFARE Classic 1K/4K: legacy access control (Android-only on iOS)
- MIFARE DESFire EV3: enterprise access, strong crypto
Testing NFC
- Physical devices only — emulators do not support NFC
- Maintain a tag library: 5–10 types per project
- Automated UI tests with mocked
NfcManagerfor logic paths - Field testing in target environments (gloves, sleeves, metal surfaces)
QR as Fallback
Older iPhones (pre-XS) and budget Android phones do not have reliable NFC. Always ship a QR-code fallback that resolves to the same payload. Print both on the tag substrate.
Conclusion
NFC + Flutter is a well-understood, production-grade combo in 2026. Pick the right tag, plan iOS vs Android capability up front, sign your payloads, and never skip the QR fallback. Done right, NFC unlocks tap-speed experiences that make your product feel magical — and most of your competitors will not bother to add them.