About
Blog Projects Work with us
Mobile · Feb 12, 2026 · ⏱ 7 min read

Android NFC Host Card Emulation (HCE) in Flutter (2026)

NFC is widely used today for contactless payments, access cards, and identity verification. But Android's Host Card Emulation (HCE) flips the script — letting your phone act as the NFC card itself.

SD
Sabin Dahal Flutter Developer · ORA Technology
NFC contactless technology on a smartphone
Flutter Android HCE NFC Technology
NFC is widely used today for things like contactless payments, access cards, and identity verification systems. Normally, we think of NFC as reading a card or tag. But Android also provides a feature called Host Card Emulation (HCE) that allows your phone to act like the NFC card itself.

This can be useful if you're building things like:

  • Digital ID cards
  • Access control systems
  • Smart ticketing solutions
  • Contactless authentication apps

In this guide, I'll walk through how to implement Android NFC Host Card Emulation in a Flutter project. The idea is simple: when an NFC reader scans the phone, the phone responds just like a smart card would.

📡
We'll create a native Android service using HostApduService, process APDU commands, and send dynamic data from Flutter using SharedPreferences.

Prerequisites

Before starting, make sure you have:

  • Flutter installed
  • Android Studio installed
  • A physical Android device with NFC support
  • Android SDK 19+ (required for HCE)
  • Basic understanding of Flutter and Android native code
⚠️
Note: NFC usually won't work properly on emulators, so you'll need a real physical device for testing.

How NFC HCE Works in Android

When an NFC reader scans your device, Android follows a specific flow:

01
Reader sends a SELECT command with an AID (Application Identifier)
02
Android checks if any app has registered that AID
03
If found, Android triggers the app's HostApduService
04
Service processes the command and returns a response to the reader

In this implementation we use:

  • HostApduService to handle NFC communication
  • SharedPreferences to receive data from Flutter
  • APDU command processing to respond to the NFC reader

Project Structure

Inside your Flutter project, the Android implementation will look something like this:

android/
└── app/
    └── src/
        └── main/
            ├── kotlin/com/example/nfcdemo/
            │   └── MyHostApduService.kt
            └── res/
                ├── xml/
                │   └── apduservice.xml
                └── values/
                    └── strings.xml

Step 1: Create the HCE Service

The main part of this implementation is the HostApduService. This service is responsible for receiving commands from the NFC reader and sending responses.

Create the following file:

android/app/src/main/kotlin/com/example/nfcdemo/MyHostApduService.kt
// Path: android/app/src/main/kotlin/com/example/nfcdemo/MyHostApduService.kt

package com.example.nfcdemo

import android.content.Context
import android.nfc.cardemulation.HostApduService
import android.os.Bundle
import android.util.Log
import android.content.SharedPreferences

class MyHostApduService : HostApduService() {

    companion object {
        const val TAG = "MyHceService"
        const val STATUS_SUCCESS = "9000"
        const val STATUS_FAILED = "6F00"
    }

    override fun onDeactivated(reason: Int) {
        Log.d(TAG, "Service Deactivated: $reason")
    }

    override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle?): ByteArray {
        val prefs = getSharedPreferences("FlutterSharedPreferences",
            Context.MODE_PRIVATE)
        val savedAID = prefs.getString("flutter.aid", null)

        if (savedAID != null && commandApdu.contentEquals(hexToByteArray(savedAID))) {
            val userId = prefs.getString("flutter.userid", "DemoUser") ?: "DemoUser"
            val cardId = prefs.getString("flutter.cardid", "CARD001") ?: "CARD001"

            val response = "$userId|$cardId".toByteArray()
            return response + hexToByteArray(STATUS_SUCCESS)
        }

        return hexToByteArray(STATUS_FAILED)
    }

    private fun hexToByteArray(hex: String): ByteArray {
        val len = hex.length
        val data = ByteArray(len / 2)
        var i = 0
        while (i < len) {
            data[i / 2] = (
                (Character.digit(hex[i], 16) shl 4) +
                Character.digit(hex[i + 1], 16)
            ).toByte()
            i += 2
        }
        return data
    }
}

What this service does

The service listens for APDU commands from the NFC reader.

If the command contains the correct AID, it:

  1. Reads values from SharedPreferences
  2. Builds a response string
  3. Sends it back to the NFC reader

For example, the response might look like this:

user123|card001

This allows Flutter to dynamically control what data the NFC reader receives.

Step 2: Configure the AID

Next, you need to tell Android which AID your service should handle. Create this file:

android/app/src/main/res/xml/apduservice.xml
<host-apdu-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/service_desc"
    android:requireDeviceUnlock="false">

    <aid-group
        android:description="@string/aid_desc"
        android:category="other">

        <aid-filter android:name="F0010203040506" />

    </aid-group>

</host-apdu-service>
💡
The aid-filter tells Android: if an NFC reader selects this AID, trigger our service. Make sure the AID here matches exactly what you use in MyHostApduService.kt and your NFC reader configuration.

Step 3: Add String Resources

Add the following strings:

android/app/src/main/res/values/strings.xml
<resources>
    <string name="service_desc">NFC HCE Service</string>
    <string name="aid_desc">Sample AID Group</string>
</resources>

Step 4: Register the Service

Finally, you need to register the service in AndroidManifest.xml:

// Path: android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.NFC" />

    <uses-feature
        android:name="android.hardware.nfc.hce"
        android:required="false" />

    <application
        android:label="NFC Demo"
        android:allowBackup="false">

        <service
            android:name=".MyHostApduService"
            android:permission="android.permission.BIND_NFC_SERVICE"
            android:exported="true">

            <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/apduservice" />

        </service>

    </application>

</manifest>

Common Issues

📵

NFC Not Triggering

Make sure NFC is enabled on the device and the device supports Host Card Emulation. You can check this in device Settings → Connected Devices → NFC.

🔇

Service Not Responding

Usually happens when the AID doesn't match between MyHostApduService.kt, apduservice.xml, and your NFC reader configuration. Verify all three match exactly.

📋

Flutter Data Not Appearing

Ensure Flutter saves values in SharedPreferences using the correct keys: flutter.userid and flutter.cardid.

Conclusion

Android Host Card Emulation is a powerful feature that allows your phone to behave like an NFC smart card. By combining it with Flutter and a small amount of native Android code, you can build applications for things like:

  • Contactless authentication
  • Digital ID systems
  • Access control
  • Smart ticketing

The key pieces are HostApduService, AID configuration, and APDU command handling. Once those are set up, your phone can respond to NFC readers just like a physical card.

Full source code for this implementation is available to ORA Technology newsletter subscribers. Subscribe below to get access and stay updated on our latest Flutter and mobile engineering guides.
SD
Sabin Dahal
Flutter Developer · ORA Technology

Sabin specialises in cross-platform mobile development with Flutter, with deep experience in Android native integrations, NFC systems, and embedded device communication. He contributes to ORA's mobile engineering practice and open-source tooling.

Back to all articles