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

如何用Flutter实现SUNMI V2S通过NFC读取安卓/iOS钱包凭证

实现SUNMI V2S Flutter读取Google Wallet/Apple Pass NFC数据

一、前置配置(SUNMI V2S + Flutter)

  • 安卓权限配置:在项目android/app/src/main/AndroidManifest.xml中添加以下权限和配置,确保APP能访问NFC并捕获前台NFC事件:
    <!-- NFC基础权限 -->
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" android:required="true" />
    <!-- 前台NFC调度权限 -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    
    <!-- 在主Activity中添加NFC intent过滤 -->
    <activity
        android:name=".MainActivity"
        ...>
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <!-- Google Wallet Pass MIME类型 -->
            <data android:mimeType="application/vnd.google.pass" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.nfc.action.TECH_DISCOVERED" />
        </intent-filter>
        <meta-data
            android:name="android.nfc.action.TECH_DISCOVERED"
            android:resource="@xml/nfc_tech_filter" />
    </activity>
    
  • 创建NFC技术过滤文件:在android/app/src/main/res/xml下新建nfc_tech_filter.xml,适配SUNMI支持的NFC标准:
    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.Ndef</tech>
            <tech>android.nfc.tech.NfcA</tech>
            <tech>android.nfc.tech.MifareClassic</tech>
            <tech>android.nfc.tech.Felica</tech>
        </tech-list>
    </resources>
    

二、核心依赖与代码实现

在pubspec.yaml中添加nfc_manager依赖:

dependencies:
  flutter:
    sdk: flutter
  nfc_manager: ^3.3.0

完整读取逻辑代码

import 'package:flutter/material.dart';
import 'package:nfc_manager/nfc_manager.dart';

class NfcPassReader extends StatefulWidget {
  const NfcPassReader({super.key});

  @override
  State<NfcPassReader> createState() => _NfcPassReaderState();
}

class _NfcPassReaderState extends State<NfcPassReader> {
  bool _nfcAvailable = false;
  bool _isScanning = false;
  String _status = '';

  @override
  void initState() {
    super.initState();
    _checkNfcStatus();
  }

  // 检查NFC是否可用
  Future<void> _checkNfcStatus() async {
    _nfcAvailable = await NfcManager.instance.isAvailable();
    setState(() {});
  }

  // 启动NFC扫描
  Future<void> _startScan() async {
    if (!_nfcAvailable) {
      setState(() => _status = '设备NFC不可用');
      return;
    }

    setState(() {
      _isScanning = true;
      _status = '请将手机钱包贴近NFC区域...';
    });

    NfcManager.instance.startSession(
      onDiscovered: (NfcTag tag) async {
        try {
          // 读取NDEF格式数据(Google/Apple Pass均基于NDEF)
          final ndef = Ndef.from(tag);
          if (ndef == null) {
            setState(() => _status = '标签不支持NDEF格式');
            NfcManager.instance.stopSession();
            return;
          }

          final message = ndef.cachedMessage;
          if (message == null) {
            setState(() => _status = '标签无有效数据');
            NfcManager.instance.stopSession();
            return;
          }

          // 遍历记录,匹配Google/Apple Pass
          for (final record in message.records) {
            // 处理Google Wallet Pass
            if (record.typeNameFormat == NdefTypeNameFormat.media &&
                String.fromCharCodes(record.type) == 'application/vnd.google.pass') {
              final passData = String.fromCharCodes(record.payload);
              setState(() => _status = '读取Google Pass成功:\n$passData');
              _activateTicket(passData);
              break;
            }

            // 处理Apple Pass(通常为URI格式记录)
            if (record.typeNameFormat == NdefTypeNameFormat.nfcWellKnown &&
                String.fromCharCodes(record.type) == 'U') {
              final passUri = Uri.parse(String.fromCharCodes(record.payload));
              setState(() => _status = '读取Apple Pass成功:\n$passUri');
              _activateTicket(passUri.toString());
              break;
            }
          }
        } catch (e) {
          setState(() => _status = '读取失败: ${e.toString()}');
        } finally {
          NfcManager.instance.stopSession();
          setState(() => _isScanning = false);
        }
      },
      options: const NfcSessionOptions(
        android: AndroidNfcSessionOptions(
          foregroundMode: true,
          readerMode: AndroidNfcReaderMode.normal,
        ),
      ),
    );
  }

  // 票证激活逻辑(替换为你的业务接口调用)
  void _activateTicket(String passInfo) {
    // 示例:调用后端接口完成激活,或触发SUNMI打印机打印凭证
    setState(() {
      _status += '\n\n票证激活成功!';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('NFC票证读取器')),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(_status, textAlign: TextAlign.center, style: const TextStyle(fontSize: 16)),
              const SizedBox(height: 24),
              ElevatedButton(
                onPressed: _isScanning ? null : _startScan,
                child: Text(_isScanning ? '扫描中...' : '开始扫描'),
              ),
              if (!_nfcAvailable)
                const Padding(
                  padding: EdgeInsets.only(top: 16),
                  child: Text('当前设备不支持NFC', style: TextStyle(color: Colors.red)),
                ),
            ],
          ),
        ),
      ),
    );
  }
}

三、SUNMI V2S适配与测试要点

  1. 开启设备NFC:进入SUNMI系统设置->NFC,确保NFC功能已启用,并允许当前APP的NFC访问权限
  2. 测试场景要求:
    • Google Wallet:打开Wallet应用并显示目标Pass,保持屏幕常亮后贴近SUNMI的NFC区域(通常在设备顶部)
    • Apple设备:解锁手机,打开Wallet应用并显示目标Pass,贴近SUNMI NFC区域
  3. 模式调整:若无法捕获Apple Pass数据,可尝试将AndroidNfcReaderMode改为cardEmulation,适配Apple使用的Felica标准

四、常见问题排查

  • 读取无响应:检查AndroidManifest权限是否配置完整,确保APP处于前台运行状态
  • 解析失败:打印完整的NDEF记录内容,确认Google/Apple Pass实际发送的记录类型,调整代码中的匹配逻辑
  • Pass无NFC数据:确认创建Google/Apple Pass时已配置NFC字段(Google Pass需添加nfc节点,Apple Pass需在pass.json中配置nfc字段)

内容的提问来源于stack exchange,提问作者f-accu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:07:16