Flutter NFC Kit

repository·develop·Indexed 42 days ago

https://github.com/nfcim/flutter_nfc_kit

A Flutter plugin providing NFC functionality for Android, iOS, and Web. It supports reading and writing NDEF records, low-level block/page/sector access for technologies like MIFARE, and raw command transceiving for ISO 7816 Smart Cards. The plugin features two operation modes: Polling (cross-platform) and Event Streaming (Android only). Web support is implemented via a specific WebUSB protocol for dual-interface devices.

Tokens
5.2K
Snippets
16
Records
24
Agent score
53%

What's inside Flutter NFC Kit

  1. Overview of the WebUSB Protocol for FlutterNfcKit

    develop

    Because NFC is not directly accessible via web browsers, applications targeting both NFC and USB interfaces can use the WebUSB protocol to communicate with Chromium-based browsers.

    Communication is performed via Control Transfers using interface index 1.

    Important: You must implement this specific protocol on your own USB hardware device before it can be used with the web version of flutter_nfc_kit. Currently, devices like CanoKey support this protocol.

  2. Understand Flutter NFC Kit operation modes

    develop

    The library provides two distinct ways to interact with NFC tags:

    1. Polling (Default): The application actively searches for tags.
    2. Event Streaming: The application listens for NFC events.

    Both modes return an NFCTag object which can be used to perform read/write operations.

  3. Customize the iOS launch screen assets

    develop

    To change the image displayed during the app's launch on iOS, you can replace the existing image files in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory with your own assets.

    Alternatively, you can manage these assets using Xcode:

    1. Open the iOS project workspace using open ios/Runner.xcworkspace.
    2. In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog to replace the launch screen images.
    open ios/Runner.xcworkspace
  4. Use Polling mode for cross-platform NFC interactions

    develop

    Polling is the default operation mode and is supported on all platforms (Android, iOS, Web). It is the recommended method to ensure consistent behavior across different operating systems.

    To use polling:

    1. Check FlutterNfcKit.nfcAvailability to ensure NFC is available.
    2. Call FlutterNfcKit.poll() to start scanning. You can provide a timeout (Android only) and iOS-specific messages (iosMultipleTagMessage, iosAlertMessage).
    3. Perform operations like reading/writing NDEF records or low-level block access based on the detected tag.type.
    4. Crucial: Always call FlutterNfcKit.finish() exactly once when your interaction is complete to release the NFC session. On iOS, you can pass iosAlertMessage or iosErrorMessage to finish() to show a final status to the user.
    import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
    import 'package:ndef/ndef.dart' as ndef;
    
    // 1. Check availability
    var availability = await FlutterNfcKit.nfcAvailability;
    if (availability != NFCAvailability.available) {
        // handle error
    }
    
    // 2. Poll for a tag
    var tag = await FlutterNfcKit.poll(
      timeout: Duration(seconds: 10), 
      iosMultipleTagMessage: "Multiple tags found!", 
      iosAlertMessage: "Scan your tag"
    );
    
    // 3. Perform operations
    if (tag.ndefAvailable) {
      for (var record in await FlutterNfcKit.readNDEFRecords(cached: false)) {
        print(record.toString());
      }
    }
    
    // 4. Finish the session
    await FlutterNfcKit.finish(iosAlertMessage: "Success");
  5. Optimize Android NFC detection performance

    develop

    On Android, you can improve tag detection speed and customize feedback using the androidReaderModeFlags parameter in the poll method. This is useful for high-speed requirements (like access control) or when you want to provide custom haptic/audio feedback instead of system sounds.

    Common flags derived from NfcAdapter:

    • 0x80 (FLAG_READER_SKIP_NDEF_CHECK): Skips automatic NDEF discovery, improving detection speed by approximately 500ms.
    • 0x100 (FLAG_READER_NO_PLATFORM_SOUNDS): Disables the system beep/vibration.
    // Skip automatic NDEF discovery for ~500ms faster detection
    // and disable platform sounds for custom feedback
    const flags = 0x80 | 0x100; // FLAG_READER_SKIP_NDEF_CHECK | FLAG_READER_NO_PLATFORM_SOUNDS
    
    final tag = await FlutterNfcKit.poll(
      androidReaderModeFlags: flags,
    );
  6. Setup Flutter NFC Kit on Android

    develop

    To use this plugin on Android, ensure your environment meets the following minimum requirements:

    • Java 17
    • Gradle 8.9
    • Android SDK 24 (ensure you set the corresponding jvmTarget in your app's build.gradle)
    • Android Gradle Plugin 8.7

    Required Permissions: You must add android.permission.NFC to your AndroidManifest.xml.

  7. Setup Flutter NFC Kit on Web

    develop
    The web version of this plugin does not support native browser NFC. Instead, it implements a specific WebUSB protocol. This allows Flutter applications to communicate with dual-interface (NFC / USB) devices in a platform-independent manner. Ensure you understand this protocol before implementation.
  8. Setup Flutter NFC Kit on iOS

    develop

    The plugin requires iOS 13+ and supports Swift Package Manager. Follow these steps to configure your iOS project:

    1. Entitlements: Add Near Field Communication Tag Reader Session Formats entitlements.
    2. Info.plist Configuration:
      • Add NFCReaderUsageDescription.
      • Add com.apple.developer.nfc.readersession.felica.systemcodes and com.apple.developer.nfc.readersession.iso7816.select-identifiers as needed.
      • CRITICAL WARNING: For iOS 14.5 and earlier, you MUST add these identifiers to Info.plist before invoking poll with readIso18092 or readIso15693 enabled. Failure to do so may cause NFC to become completely unavailable until a device reboot due to a CoreNFC bug.
    3. Xcode Configuration:
      • Open Runner.xcworkspace in Xcode.
      • Navigate to Project Settings > Signing & Capabilities.
      • Select the Runner target.
      • Click + Capability and select Near Field Communication Tag Reading.
  9. Set up Event Streaming on Android

    develop

    Event streaming allows your app to receive NFC tag events even when in the foreground without active polling. This is currently Android only.

    Implementation Steps

    1. Modify Android MainActivity: Extend FlutterActivity and override onResume, onPause, and onNewIntent. You must use enableForegroundDispatch to capture tags and call FlutterNfcKitPlugin.handleTag(tag) in onNewIntent.

    2. Update AndroidManifest.xml: Ensure your custom MainActivity is set as the main activity.

    3. Listen in Flutter: Use FlutterNfcKit.tagStream.listen((tag) { ... }) to process incoming tags.

    WARNING: When using tagStream, DO NOT call FlutterNfcKit.finish().

    // 1. Android Kotlin Setup (MainActivity.kt)
    class MainActivity : FlutterActivity() {
        override fun onResume() {
            super.onResume()
            val adapter: NfcAdapter? = NfcAdapter.getDefaultAdapter(this)
            val pendingIntent: PendingIntent = PendingIntent.getActivity(
                this, 0, Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE
            )
            adapter?.enableForegroundDispatch(this, pendingIntent, null, null)
        }
    
        override fun onPause() {
            super.onPause()
            val adapter: NfcAdapter? = NfcAdapter.getDefaultAdapter(this)
            adapter?.disableForegroundDispatch(this)
        }
    
        override fun onNewIntent(intent: Intent) {
            val tag: Tag? = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
            tag?.apply(FlutterNfcKitPlugin::handleTag)
        }
    }
    
    // 2. Flutter Dart Setup
    @override
    void initState() {
      super.initState();
      FlutterNfcKit.tagStream.listen((tag) {
        print('Tag detected: ${tag.id}');
        // Process tag here
        // DO NOT call FlutterNfcKit.finish() in this mode!
      });
    }
  10. Web implementation requirements for Flutter NFC Kit

    develop

    The web implementation of flutter_nfc_kit relies on the WebUSB protocol. To use NFC functionality in a web browser, you must implement this specific protocol on your NFC device side.

    Important: Do not use the FlutterNfcKitWeb class directly in your application code. Instead, use the standard FlutterNfcKit class provided by the package. The web implementation is automatically registered and used when running on a web platform.

    For detailed protocol specifications, refer to the WebUSB.md documentation in the repository.

  11. Read and write NDEF records

    develop

    Once a tag is detected via polling, you can interact with NDEF (NFC Data Exchange Format) records.

    Reading

    • Decoded Records: Use readNDEFRecords(cached: bool) to get high-level ndef.NDEFRecord objects.
    • Raw Records: Use readNDEFRawRecords(cached: bool) to get raw data in hex string format.

    Writing

    • Decoded Records: Use writeNDEFRecords([List<ndef.NDEFRecord>]) to write structured data (e.g., a URI).
    • Raw Records: Use writeNDEFRawRecords([List<NDEFRawRecord>]) to write raw hex data.

    Note: Always check tag.ndefAvailable before reading and tag.ndefWritable before writing.

    // Reading decoded records
    if (tag.ndefAvailable) {
      for (var record in await FlutterNfcKit.readNDEFRecords(cached: false)) {
        print(record.toString());
      }
    }
    
    // Writing decoded records
    if (tag.ndefWritable) {
      await FlutterNfcKit.writeNDEFRecords([
        new ndef.UriRecord.fromUriString("https://github.com/nfcim/flutter_nfc_kit")
      ]);
    }
  12. Perform low-level block and sector operations

    develop

    For specific tag types, you can perform low-level data access:

    • ISO 15693: Use writeBlock(int index, List<int> data, ...) to write to specific blocks. You can configure iso15693RequestFlag and iso15693ExtendedMode.
    • Mifare Classic: Use authenticateSector(int sector, {String? keyA}) followed by readSector(int sector) or readBlock(int block).
    • ISO 7816: Use transceive(String command, Duration timeout) for APDU commands (Note: timeout is Android-only).
    // ISO 15693 block write
    if (tag.type == NFCTagType.iso15693) {
      await FlutterNfcKit.writeBlock(
        1, 
        [0xde, 0xad, 0xbe, 0xff], 
        iso15693RequestFlag: Iso15693RequestFlag(),
        iso15693ExtendedMode: false
      );
    }
    
    // Mifare Classic sector access
    if (tag.type == NFCType.mifare_classic) {
      await FlutterNfcKit.authenticateSector(0, keyA: "FFFFFFFFFFFF");
      var data = await FlutterNfcKit.readSector(0);
    }