You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

尝试擦除NFC-A标签时遇到数据未更改问题

NFC标签擦除失败的问题排查与修复

针对你使用nfc_manager库擦除NFC标签用户存储页(第4至39页)无效的问题,以下是核心原因分析和修复方案:

核心问题点

  1. 多数NFC标签需先解锁写入权限:比如MIFARE Classic这类常用标签,用户存储区(通常第4页及以后)默认受密钥保护,直接写入会被拒绝,原代码缺少密钥验证步骤。
  2. 未校验写入命令的响应结果:transceive方法会返回标签的响应数据,写入成功/失败都有对应返回码,当前代码未做校验,无法定位出错环节。
  3. 会话生命周期管理不当:原代码在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:57:17