nfc_util 2.2.0
nfc_util: ^2.2.0 copied to clipboard
A Flutter plugin providing access to NFC features on Android and iOS.
2.2.0 #
The rest of the nfc_manager 4.x catch-up. Android-only additions; nothing is breaking.
Added #
-
NfcManager.onAdapterStateChanged, aStream<NfcAdapterState>that emits when the user switches NFC on or off in system settings. Pairs withcheckAvailability(): check once at startup, then react to changes instead of polling. The stream does not replay the current state. On iOS it never emits — there is no NFC toggle to watch.The receiver is registered against the application context, so a configuration change does not churn it, and it is unregistered on activity and engine detach. (Upstream registers an equivalent receiver and never unregisters it, leaking one per attach cycle.)
-
NfcBarcodetag class and thestartSession(discoverNfcBarcode:)flag that makes it reachable. Barcode (Kovio) tags are only discovered whenFLAG_READER_NFC_BARCODEis set, so the flag is the point —nfc_managerships the tag class but never sets the flag fromstartSession, leaving the class unreachable through its own API. The class carriesidentifier,barcodeTypeandbarcode, and has no operations, becauseandroid.nfc.tech.NfcBarcodehas none. -
startSession(skipNdefCheck:)setsFLAG_READER_SKIP_NDEF_CHECK, which makes discovery measurably faster by skipping the platform's NDEF probe.Ndef.from(tag)then returns null, so use it only when the tag's NDEF content does not interest you. -
NfcManager.isSecureNfcSupported()/isSecureNfcEnabled(). Secure NFC restricts tag reading to an unlocked device. Android API 29+; both return false below that and on iOS. -
MifareClassic.blockToSector()/sectorToBlock()/getBlockCountInSector(). Sector geometry is not uniform — a 4K card has 32 sectors of 4 blocks followed by 8 of 16 — so this arithmetic cannot be done by hand fromblockCountandsectorCount. These resolve the technology without opening a connection, since they read a static description rather than talking to the tag. (Upstream implements all three natively but never exposed them in Dart, so they are unreachable there.)
2.1.0 #
Catches up with the useful parts of nfc_manager 4.x. Nothing here is breaking: existing
code compiles and behaves as it did.
Added #
-
NfcManager.checkAvailability()returnsNfcAvailability.enabled,.disabledor.unsupported.isAvailable()could only say "no", which conflated a device with no NFC hardware and a device where the user has switched NFC off — so an app could not tell whether pointing the user at system settings would help. Android reports all three; iOS never reports.disabled, because CoreNFC has no such state.isAvailable()still works but is deprecated and will be removed in 3.0.0. -
NfcError.codecarries the exact CoreNFC failure as a newNfcReaderErrorCodeenum (24 CoreNFC codes plusunknown). The iOS side previously mapped 3 of CoreNFC's 22 and dropped the rest, so a lost tag, a disabled radio, a too-small tag and a security violation all arrived asNfcErrorType.unknownwith nothing to tell them apart.NfcErrorTypeitself is unchanged — widening it would have broken exhaustiveswitchstatements — sotypestays the coarse category andcodecarries the detail. Null on Android, and null on iOS for failures that did not come from CoreNFC.The two codes added in the iOS 26 SDK (
ineligible,accessNotAccepted) are matched by raw value, so the plugin still compiles against older SDKs. An unrecognised code decodes toNfcReaderErrorCode.unknownrather than throwing. -
setTimeout/getTimeout/getMaxTransceiveLengthon the Android tag classes.timeoutandmaxTransceiveLengthare snapshots taken at discovery, so the timeout could be read but never changed — there was no way to give a slow tag more time before the exchange failed astag_lost.setTimeoutandgetTimeoutare onIsoDep,NfcA,NfcF,MifareClassicandMifareUltralight;getMaxTransceiveLengthis on those plusNfcBandNfcV.android.nfc.techoffers no timeout accessor for NfcB and NfcV. The existing fields are untouched. -
startSession(noPlatformSounds:)(Android).FLAG_READER_NO_PLATFORM_SOUNDSwas applied unconditionally, so the system tag-discovery sound could never play and there was no way to ask for it. It is now a parameter. It defaults totrue, matching what every release before this one did — note this is the opposite ofnfc_manager, which defaults it tofalse. PassnoPlatformSounds: falseto let the sound play. -
Ndef.canMakeReadOnly(Android). The native side already reported this value; it was only reachable by digging throughadditionalData. It is now a proper field, and no longer appears inadditionalData. Null on iOS, which does not report it. Check it before callingwriteLock()— not every tag can be locked.
Fixed #
- iOS: starting a session while one is already running is now rejected with a
session_already_existsPlatformException. The second session used to overwrite the first, which was then never invalidated: its reader sheet stayed up and its tags leaked for the lifetime of the process.
2.0.0 #
Requirements #
- Minimum Flutter is now 3.44.0. That is the first stable release which stages the
FlutterFrameworkSwift package, whichios/nfc_util/Package.swiftnow depends on as the Flutter tool requires. - Minimum iOS deployment target is 15.6, declared by both the podspec and
Package.swift.
Breaking #
- Removed the unused
flutter createboilerplate:NfcUtilPlatform,MethodChannelNfcUtiland theplugin_platform_interfacedependency. These were never part of the NFC API. Ndef.read()now returnsFuture<NdefMessage?>. A tag with no NDEF message previously crashed with aTypeErrorinstead of reporting "nothing written yet".MifareClassic.transceivetakesUint8List datainstead ofint data.- The Objective-C shim (
NfcUtilPlugin.h/.m) is gone and the Swift class is now namedNfcUtilPlugininstead ofSwiftNfcUtilPlugin. Swift Package Manager does not support mixed-language targets, and the shim only existed to expose the Swift class to Objective-C. Nothing referenced these types from application code.
Fixed #
-
iOS builds work again under Swift Package Manager.
Package.swiftdeclared its sources atClasses/, which does not exist inside the package root, so the target was empty andxcodebuildrefused to resolve it. Since Flutter 3.44 enables SPM by default, this failed every iOS build. The native sources now live atios/nfc_util/Sources/nfc_util/, shared by both the podspec andPackage.swift. -
Android: implemented every tag I/O method.
Ndef#read/write/writeLock,NfcA/NfcB/NfcF/NfcV/IsoDep#transceive, all nineMifareClassiccommands, the threeMifareUltralightcommands andNdefFormatable#format/formatReadOnlypreviously threwMissingPluginException. Tag I/O runs on a dedicated thread and reuses the open connection, so aMifareClassicsector authentication still holds for the followingreadBlock. -
iOS: tag I/O inside
onDiscoveredworks again. 1.0.3 invalidated the session as soon as a tag was serialized, soNdef.read()/write(),Iso7816.sendCommand()and friends failed with "Tag is not found". WithinvalidateAfterFirstRead: truethe session now stays open until the app callsstopSession(). -
iOS:
Nfc#stopSessionno longer submits its Flutter reply twice whenerrorMessageis given (missingreturn), which also invalidated the session twice. -
iOS: an unrecognized tag type left the reader sheet hanging until the 60 s timeout because the completion handler was never called.
-
iOS: starting a session with an unusable polling option reported success while no session had begun; it now returns an
unavailableerror. -
iOS: the tag map is written from the CoreNFC delegate queue and read from the platform thread; access is now serialized.
-
FeliCa.requestSpecificationVersion()invokedFeliCa#requestSpecificationVersionResponse, which no platform implements, so it always threwMissingPluginException. -
onDiscoveredcallbacks that throw no longer leak the native tag handle, and the error is no longer swallowed as an unhandled async error. -
Decoding unknown NDEF type-name-format, MiFare family or error-type values from the platform threw
StateError; these now fall back to theirunknownvariants. Records read off a tag also skip the creation-time format validation, which rejected legitimate chunked records. -
Android: reader mode is now disabled and the tag map cleared on
stopSessionand on activity/engine detach. Reader mode previously stayed active, keeping the NFC radio polling and holding a reference to a destroyed activity. -
Android: an empty or unrecognized
pollingOptionslist produced zero technology flags, which started a session that could never discover a tag. It now falls back to every supported technology.
Documentation #
- Documented the most common iOS setup mistake, found while testing on a device: because
startSessionpollsiso18092by default, CoreNFC rejects the session withMissing required entitlementunlesscom.apple.developer.nfc.readersession.felica.systemcodesis in the app'sInfo.plist. The reader sheet simply never appears, andstartSessionstill completes normally because iOS reports the failure asynchronously -- so an app without anonErrorcallback sees nothing at all. The example now sets the key, passesonErroron every session, and gained a "Tag I/O" button that exercises the tag commands on both platforms.
Changed #
- Android: tag errors distinguish the connect step from the command itself
(
connect: TagLostException: ...), and failures are logged under theNfcUtilPlugintag. A tag that answers discovery but cannot be connected is otherwise indistinguishable from a command that failed. - Android: dropped the leftover debug logging from
startSession. - Android: reader mode sets
FLAG_READER_NO_PLATFORM_SOUNDSand a 250 ms presence-check delay. - Platform value decoding uses constant reverse maps instead of a linear
firstWherescan per record. - Calls whose result is required now raise a
PlatformException(code: 'no_result')instead of force-unwrapping;NfcManager.isAvailable()returnsfalserather than throwing. - iOS: dropped the
@available(iOS 13.0, *)scaffolding, now that the deployment target is 15.6. - CocoaPods builds ship
PrivacyInfo.xcprivacy, matching what Swift Package Manager builds already did.
1.0.3 #
- iOS first scan bug fixed.
1.0.2 #
- Swift Package Manager support
1.0.0 #
- flutter: min 3.32.0
0.1.1 #
- Nfc#disposeTag fixed.
0.1.0 #
- Android and iOS are done.
0.0.1 #
- TODO: Describe initial release.