nfc_tag_reader 0.1.0
nfc_tag_reader: ^0.1.0 copied to clipboard
Hand-written Android NFC tag reader for Flutter. Streams scanned tags with decoded NDEF text, URI and raw records over platform channels.
nfc_tag_reader #
A small, hand-written Flutter plugin that reads NFC tags on Android. No
off-the-shelf NFC package is used — the bridge is a Kotlin implementation on top
of NfcAdapter.enableReaderMode, wired to Dart through a MethodChannel and an
EventChannel.
It decodes multiple NDEF record types (plain text, URI, MIME, external, empty) and always exposes the raw payload bytes, so it is useful for real work (contactless cards, tags, ID) rather than a single-tag toy.
Why this exists #
I hit a native-only problem in an app (reading contactless NFC tags), solved it with a hand-written Flutter ↔ Kotlin channel, then extracted the bridge into this reusable plugin. NFC read maps directly to fintech use cases: contactless cards, tags and ID.
Features #
isAvailable()— is NFC present and switched on?startSession()/stopSession()— reader-mode lifecycle, tied correctly to the Android Activity lifecycle (no leaks, survives rotation).Stream<NfcTag> tags— one structured event per tap.- Typed models —
NfcTag,NdefRecord,NdefRecordType,NfcException. Nodynamicleaks to consumers. - Graceful handling of "no NFC hardware" and "NFC turned off".
Supported record types #
| Record | NdefRecordType |
Decoded field |
|---|---|---|
| Well-known text (RTD_TEXT) | text |
text, languageCode |
| Well-known URI (RTD_URI) | uri |
uri |
| Absolute URI | uri |
uri |
| MIME media | mime |
mimeType (+ raw payload) |
| External type | external |
mimeType (+ raw payload) |
| Empty | empty |
— |
| Anything else | unknown |
raw payload |
Install #
dependencies:
nfc_tag_reader: ^0.1.0
Then:
flutter pub get
Android setup #
The plugin's manifest already contributes the NFC permission, so it is merged into your app automatically:
<uses-permission android:name="android.permission.NFC" />
If you want the Play Store to hide your app on devices without NFC hardware, add
the feature declaration to your app's AndroidManifest.xml (the plugin
intentionally does not force this):
<uses-feature android:name="android.hardware.nfc" android:required="true" />
minSdkVersion must be 19 or higher (enableReaderMode requirement). The
plugin targets minSdk 24.
Usage #
import 'package:nfc_tag_reader/nfc_tag_reader.dart';
final nfc = NfcTagReader();
Future<void> scanOnce() async {
if (!await nfc.isAvailable()) return; // no NFC, or it is turned off
final sub = nfc.tags.listen(
(tag) {
for (final record in tag.records) {
print(record.describe()); // e.g. "URI: https://example.com"
}
},
onError: (Object e) {
if (e is NfcException && e.reason == NfcErrorReason.disabled) {
// prompt the user to enable NFC
}
},
);
try {
await nfc.startSession();
} on NfcException catch (e) {
// e.reason: notSupported / disabled / noActivity
}
// ... when done:
await nfc.stopSession();
await sub.cancel();
}
A complete, fintech-styled example lives in example/.
Architecture #
Dart side
NfcTagReader
└─▶ NfcTagReaderPlatform (interface — leaves the iOS seam)
└─▶ MethodChannelNfcTagReader
├─ MethodChannel com.abdurrahmanjun/nfc_tag_reader/methods
└─ EventChannel com.abdurrahmanjun/nfc_tag_reader/tags
Kotlin side
NfcTagReaderPlugin
├─▶ NfcAdapter.enableReaderMode (reader-mode lifecycle)
└─▶ NdefParser (Tag → structured map)
The two channels bridge the sides: method calls (startSession / stopSession
/ isAvailable) flow over the MethodChannel, and each scanned tag is pushed
back to Dart as one event over the EventChannel.
Limitations #
- Read-only. Writing NDEF messages is not implemented.
- Android only today. The federated
NfcTagReaderPlatforminterface leaves a clean seam for an iOS Core NFC implementation. - Uses the tag's cached NDEF message (no blocking IO on the reader thread), so it does not currently read non-NDEF proprietary card data.
Roadmap #
- ❌ iOS support via Core NFC (
NFCTagReaderSession). - ❌ NDEF write support.
- ❌ Opt-in deep read for specific tech types (
IsoDep,MifareClassic).
License #
Built by Abdurrahman Jundullah M — mobile engineer (Flutter + native Android), 8+ years, fintech/banking background.