如何在Flutter(Android平台)的主机卡模拟(HCE)中写入NDEF消息?
Hey there! Let's work through your NFC NDEF payload sending problem for your Android Flutter pass wallet app. Since Google Smart Tap isn't an option, and the nfc_emulator library fell short (no NDEF write support, plus it didn't work for you), here's a practical, reliable approach to get this done:
nfc_manager Library This library has solid support for Android NFC operations—including Host Card Emulation (HCE) which lets your phone act as an NDEF tag for scanning devices. It’s actively maintained and handles NDEF reads/writes seamlessly.
Step 1: Add the Dependency
First, add the library to your pubspec.yaml:
dependencies: flutter: sdk: flutter nfc_manager: ^3.3.0
Run flutter pub get to install it.
Step 2: Configure Android Manifest
Update your android/app/src/main/AndroidManifest.xml to enable NFC permissions and HCE support:
<!-- Required NFC permission --> <uses-permission android:name="android.permission.NFC" /> <!-- Declare NFC hardware requirement (adjust to "false" if you want to support non-NFC devices) --> <uses-feature android:name="android.hardware.nfc" android:required="true" /> <!-- Add HCE service configuration inside the <application> tag --> <service android:name="dev.nfc_manager.platform.android.NfcHostApduService" 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/apduservice" /> </service>
Next, create a new file android/app/src/main/res/xml/apduservice.xml with this content (defines the AIDs your HCE service will respond to):
<?xml version="1.0" encoding="utf-8"?> <host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/app_name" android:requireDeviceUnlock="false"> <aid-group android:description="NDEF AIDs" android:category="other"> <aid-filter android:name="F0010203040506" /> <aid-filter android:name="D2760000850101" /> <!-- Standard NDEF AID --> </aid-group> </host-apdu-service>
Step 3: Implement NDEF Payload Sending in Flutter
Here’s a complete code snippet that checks NFC availability, sets up HCE, and sends your string as an NDEF payload when a scanning device connects:
import 'package:flutter/material.dart'; import 'package:nfc_manager/nfc_manager.dart'; class NfcSenderScreen extends StatefulWidget { const NfcSenderScreen({super.key}); @override State<NfcSenderScreen> createState() => _NfcSenderScreenState(); } class _NfcSenderScreenState extends State<NfcSenderScreen> { bool _isNfcAvailable = false; bool _isSending = false; @override void initState() { super.initState(); _checkNfcAvailability(); } Future<void> _checkNfcAvailability() async { final available = await NfcManager.instance.isAvailable(); setState(() => _isNfcAvailable = available); } Future<void> _sendNdefMessage(String text) async { if (!_isNfcAvailable) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("NFC不可用,请先开启设备NFC")), ); return; } setState(() => _isSending = true); try { // Create an NDEF message with your text payload final ndefMessage = NdefMessage([ NdefRecord.createText(text), // Optional: Add other record types if needed (e.g., URI) // NdefRecord.createUri(Uri.parse("https://example.com")), ]); // Start NFC session to listen for scanning devices await NfcManager.instance.startSession( onDiscovered: (NfcTag tag) async { try { final ndef = Ndef.from(tag); if (ndef != null && ndef.isWritable) { await ndef.write(ndefMessage); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("NDEF消息发送成功!")), ); } else { throw Exception("扫描设备不支持NDEF写入"); } } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("发送失败: ${e.toString()}")), ); } finally { await NfcManager.instance.stopSession(); if (mounted) setState(() => _isSending = false); } }, ); } catch (e) { if (mounted) { setState(() => _isSending = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("初始化NFC失败: ${e.toString()}")), ); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("NFC发送器")), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ if (!_isNfcAvailable) const Text("请开启设备NFC功能", style: TextStyle(color: Colors.red)), const SizedBox(height: 20), ElevatedButton( onPressed: _isSending ? null : () => _sendNdefMessage("你的通行证字符串内容"), child: _isSending ? const CircularProgressIndicator(color: Colors.white) : const Text("开始发送NFC消息"), ), ], ), ), ); } }
- HCE Compatibility: Ensure your Android device runs Android 4.4 (API level 19) or higher—most modern devices support HCE.
- NFC Toggle: Always check if NFC is enabled before attempting to send messages; prompt users to turn it on if not.
- Test with Real Devices: Emulators don’t support NFC, so test with two physical Android devices (or a dedicated NFC scanner).
- Custom AIDs: If your scanning device expects a specific AID, update the
apduservice.xmlwith the correct aid-filter value. - Native Backup: If the library still has issues, you can build a custom Android HCE service and call it via Flutter’s Method Channel for full control over the NFC logic.
内容的提问来源于stack exchange,提问作者ungali

