尝试擦除NFC-A标签时遇到数据未更改问题
NFC标签擦除失败的问题排查与修复
针对你使用nfc_manager库擦除NFC标签用户存储页(第4至39页)无效的问题,以下是核心原因分析和修复方案:
核心问题点
- 多数NFC标签需先解锁写入权限:比如MIFARE Classic这类常用标签,用户存储区(通常第4页及以后)默认受密钥保护,直接写入会被拒绝,原代码缺少密钥验证步骤。
- 未校验写入命令的响应结果:
transceive方法会返回标签的响应数据,写入成功/失败都有对应返回码,当前代码未做校验,无法定位出错环节。 - 会话生命周期管理不当:原代码在
onDiscovered的finally块直接标记扫描结束,可能导致写入操作未完成就终止NFC会话。
修复后的代码实现
下面是调整后的NfcService类,补充密钥验证、响应校验、完善会话管理逻辑:
import 'package:nfc_manager/nfc_manager.dart'; import 'package:nfc_manager/platform_tags.dart'; import 'dart:typed_data'; class NfcService { final NfcManager _nfcManager = NfcManager.instance; bool _isScanning = false; Future<void> eraseTag() async { if (_isScanning) return; _isScanning = true; try { await _nfcManager.startSession( onDiscovered: (NfcTag tag) async { bool operationSuccess = false; try { final nfca = NfcA.from(tag); if (nfca == null) { print('不支持NFC-A类型的标签'); return; } // 步骤1:验证扇区密钥(以MIFARE Classic默认密钥A为例,按需替换为你的标签密钥) // 第4-39页对应扇区1到扇区9(每个扇区含4页) for (int sector = 1; sector <= 9; sector++) { final authResponse = await nfca.transceive( data: Uint8List.fromList([0x60, sector * 4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), ); // 验证成功返回0x0A,失败则终止操作 if (authResponse.isEmpty || authResponse.first != 0x0A) { print('扇区$sector 密钥验证失败'); return; } } // 步骤2:逐页写入0x00(补全为4字节,符合多数标签写入要求) for (int page = 4; page < 40; page++) { final writeResponse = await nfca.transceive( data: Uint8List.fromList([0xA0, page, 0x00, 0x00, 0x00, 0x00]), ); // 写入成功返回0x0A,失败则打印错误并终止 if (writeResponse.isEmpty || writeResponse.first != 0x0A) { print('第$page页写入失败,响应:${writeResponse.map((b) => b.toRadixString(16)).join(' ')}'); return; } print('第$page页写入成功'); } operationSuccess = true; print('标签擦除完成'); } catch (e) { print('擦除操作异常:${e.toString()}'); } finally { // 操作完成后统一结束会话 await _nfcManager.stopSession(errorMessage: operationSuccess ? null : '擦除失败'); _isScanning = false; } }, ); } catch (e) { print('启动NFC会话失败:${e.toString()}'); _isScanning = false; } } // 保留读取逻辑并扩展读取范围 Future<void> startScanning( Function(String) onDataRead, Function(String) onError, ) async { if (_isScanning) return; _isScanning = true; try { await _nfcManager.startSession( onDiscovered: (NfcTag tag) async { try { final nfca = NfcA.from(tag); if (nfca != null) { try { const int startPage = 4; const int maxPages = 40; // 扩展到第39页,方便验证擦除结果 StringBuffer allData = StringBuffer(); for (int page = startPage; page < maxPages; page++) { try { final response = await nfca.transceive( data: Uint8List.fromList([0x30, page]), ); String hexData = response .map((byte) => byte.toRadixString(16).padLeft(2, '0')) .join(' '); print('Page $page raw data (hex): $hexData'); allData.writeln(hexData); } catch (e) { print('读取第$page页失败:${e.toString()}'); } } onDataRead(allData.toString()); } catch (e) { onError('收发操作异常:${e.toString()}'); } } else { onError('未检测到NFC-A标签'); } } catch (e) { onError('读取NFC标签异常:${e.toString()}'); } finally { await _nfcManager.stopSession(); _isScanning = false; } }, ); } catch (e) { onError('启动NFC会话失败:${e.toString()}'); _isScanning = false; } } void stopScanning() { if (_isScanning) { _nfcManager.stopSession(); _isScanning = false; } } }
关键修改说明
- 密钥验证:针对MIFARE Classic标签,逐扇区验证密钥(示例用默认密钥
FFFFFFFFFFFF,如果你的标签使用自定义密钥,需要替换)。 - 响应校验:每次写入和验证操作后检查标签返回的
0x0A成功码,失败则终止并打印错误信息。 - 写入数据补全:将原代码的3字节写入数据补全为4字节,符合多数NFC标签的写入格式要求。
- 会话管理优化:在所有操作完成后统一调用
stopSession,确保写入操作全部执行完毕。 - 读取范围扩展:调整读取逻辑的
maxPages到40,方便验证第4-39页的擦除结果。
额外注意事项
- 如果你的标签是MIFARE Ultralight,无需密钥验证,但仍需保证写入数据为4字节。
- 不同NFC标签的页地址范围、命令格式可能存在差异,建议参考对应标签的官方文档确认细节。
内容的提问来源于stack exchange,提问作者Mohammed Elyaakoubi
相关产品推荐
相关产品推荐

