144 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Dart
		
	
	
		
		
			
		
	
	
			144 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Dart
		
	
	
|  | import 'dart:convert'; | |||
|  | import 'dart:io'; | |||
|  | import 'dart:typed_data'; | |||
|  | import 'package:flutter/material.dart'; | |||
|  | import 'package:nfc_manager/nfc_manager.dart'; | |||
|  | import 'package:nfc_manager/ndef_record.dart'; | |||
|  | import 'package:nfc_manager/nfc_manager_android.dart'; | |||
|  | import 'package:nfc_manager/nfc_manager_ios.dart'; | |||
|  | import 'package:nfc_manager_ndef/nfc_manager_ndef.dart'; | |||
|  | 
 | |||
|  | class NfcTestPage extends StatefulWidget { | |||
|  |   const NfcTestPage({Key? key}) : super(key: key); | |||
|  | 
 | |||
|  |   @override | |||
|  |   _NfcTestPageState createState() => _NfcTestPageState(); | |||
|  | } | |||
|  | 
 | |||
|  | class _NfcTestPageState extends State<NfcTestPage> { | |||
|  |   String _nfcData = '未扫描到标签。'; | |||
|  |   bool _scanning = false; | |||
|  | 
 | |||
|  |   void _safeSetState(String data) { | |||
|  |     if (!mounted) return; | |||
|  |     setState(() => _nfcData = data); | |||
|  |   } | |||
|  | 
 | |||
|  |   Future<void> _startNfcSession() async { | |||
|  |     setState(() { | |||
|  |       _scanning = true; | |||
|  |       _nfcData = '扫描中...'; | |||
|  |     }); | |||
|  | 
 | |||
|  |     final available = await NfcManager.instance.isAvailable(); | |||
|  |     if (!available) { | |||
|  |       _safeSetState('此设备不支持 NFC'); | |||
|  |       setState(() => _scanning = false); | |||
|  |       return; | |||
|  |     } | |||
|  | 
 | |||
|  |     NfcManager.instance.startSession( | |||
|  |       onDiscovered: (NfcTag tag) async { | |||
|  |         String text = '未知'; | |||
|  | 
 | |||
|  |         try { | |||
|  |           final ndef = Ndef.from(tag); | |||
|  |           if (ndef == null) { | |||
|  |             _safeSetState('标签不支持 NDEF'); | |||
|  |           } else { | |||
|  |             final msg = ndef.cachedMessage; | |||
|  |             if (msg == null || msg.records.isEmpty) { | |||
|  |               _safeSetState('未找到任何 NDEF 记录'); | |||
|  |             } else { | |||
|  |               // 第一条记录
 | |||
|  |               final record = msg.records.first; | |||
|  |               text = _parseTextRecord(record); | |||
|  |               _safeSetState('读取到文本: "$text"'); | |||
|  | 
 | |||
|  |             } | |||
|  |           } | |||
|  |           String uid = '未知'; | |||
|  |           try { | |||
|  |             if (Platform.isAndroid) { | |||
|  |               final nfcA = NfcAAndroid.from(tag); | |||
|  |               if (nfcA != null) { | |||
|  |                 // 正确地通过 nfcA.tag.identifier 拿到 List<int>
 | |||
|  |                 final bytes = nfcA.tag.id; | |||
|  |                 uid = bytes | |||
|  |                     .map((b) => b.toRadixString(16).padLeft(2, '0')) | |||
|  |                     .join(':') | |||
|  |                     .toUpperCase(); | |||
|  |               } | |||
|  |             } else if (Platform.isIOS) { | |||
|  |               // iOS 平台,用 MiFareIos(对于大多数 ISO14443-A,也可以用 Iso7816Ios)
 | |||
|  |               final mifare = MiFareIos.from(tag); | |||
|  |               final bytes = mifare!.identifier; | |||
|  |               uid = bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':').toUpperCase(); | |||
|  |             } | |||
|  |           } catch (e) { | |||
|  |             debugPrint('取 UID 错误: $e'); | |||
|  |           } finally { | |||
|  |             setState(() => _nfcData = '标签 UID: $uid' + '\n读取到文本: "$text"'); | |||
|  |             await NfcManager.instance.stopSession(); | |||
|  |           } | |||
|  |         } catch (e) { | |||
|  |           _safeSetState('解析时出错: $e'); | |||
|  |         } finally { | |||
|  |           await NfcManager.instance.stopSession(); | |||
|  |           if (mounted) setState(() => _scanning = false); | |||
|  |         } | |||
|  |       }, | |||
|  |       pollingOptions: {NfcPollingOption.iso14443}, | |||
|  |     ); | |||
|  |   } | |||
|  | 
 | |||
|  |   /// 解析一个 NDEF Text 记录的 payload,返回纯文本内容
 | |||
|  |   String _parseTextRecord(NdefRecord record) { | |||
|  |     final payload = record.payload; | |||
|  |     if (payload.isEmpty) return ''; | |||
|  | 
 | |||
|  |     final statusByte = payload[0]; | |||
|  |     final langCodeLen = statusByte & 0x3F; | |||
|  |     // 文本数据开始的位置
 | |||
|  |     final textBytes = payload.sublist(1 + langCodeLen); | |||
|  | 
 | |||
|  |     try { | |||
|  |       return utf8.decode(textBytes);  // 默认 UTF‑8
 | |||
|  |     } catch (e) { | |||
|  |       // 解码失败时,退回到直接展示 bytes
 | |||
|  |       return String.fromCharCodes(textBytes); | |||
|  |     } | |||
|  |   } | |||
|  | 
 | |||
|  |   @override | |||
|  |   void dispose() { | |||
|  |     NfcManager.instance.stopSession(); | |||
|  |     super.dispose(); | |||
|  |   } | |||
|  | 
 | |||
|  |   @override | |||
|  |   Widget build(BuildContext context) { | |||
|  |     return Scaffold( | |||
|  |       appBar: AppBar(title: const Text('NFC 测试')), | |||
|  |       body: Padding( | |||
|  |         padding: const EdgeInsets.all(16.0), | |||
|  |         child: Column( | |||
|  |           crossAxisAlignment: CrossAxisAlignment.stretch, | |||
|  |           children: [ | |||
|  |             ElevatedButton( | |||
|  |               onPressed: _scanning ? null : _startNfcSession, | |||
|  |               child: Text(_scanning ? '扫描中...' : '开始扫描 NFC'), | |||
|  |             ), | |||
|  |             const SizedBox(height: 24), | |||
|  |             Expanded( | |||
|  |               child: SingleChildScrollView( | |||
|  |                 child: Text(_nfcData, style: const TextStyle(fontSize: 16)), | |||
|  |               ), | |||
|  |             ), | |||
|  |           ], | |||
|  |         ), | |||
|  |       ), | |||
|  |     ); | |||
|  |   } | |||
|  | } |