Open3
Flutter NFC
NFCカードをスキャンしたい
こちらのパッケージを使用するとできるようだ?
iOSは設定が大変
x-codeでこちらを追加しておく。
AppleDeveloperのアカウントが必要だった。
Apple DeveloperのサイトでNFCの設定をしておく。
Androidの設定はこれだけ
<manifest>
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
</manifest>
TEST
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/platform_tags.dart';
void main() {
(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: NfcReaderScreen(),
debugShowCheckedModeBanner: false,
);
}
}
class NfcReaderScreen extends StatefulWidget {
const NfcReaderScreen({super.key});
State<NfcReaderScreen> createState() => _NfcReaderScreenState();
}
class _NfcReaderScreenState extends State<NfcReaderScreen> {
bool isAvailable = false;
bool isReading = false;
String cardId = '';
String errorMessage = '';
String debugInfo = '';
void initState() {
super.initState();
_checkNfcAvailability();
}
Future<void> _checkNfcAvailability() async {
debugLog('Checking NFC availability...');
try {
final availability = await NfcManager.instance.isAvailable();
debugLog('NFC Availability: $availability');
setState(() {
isAvailable = availability;
if (!availability) {
errorMessage = 'NFCが無効になっています。\n設定でNFCを有効にしてください。';
} else {
errorMessage = '';
}
});
} catch (e) {
debugLog('NFC Check Error: $e');
setState(() {
isAvailable = false;
errorMessage = 'NFCの確認中にエラーが発生しました: $e';
});
}
}
void debugLog(String message) {
if (message.isEmpty) return;
print(message);
setState(() {
debugInfo += '$message\n';
});
}
Future<void> startNfcReading() async {
if (!isAvailable) {
setState(() {
errorMessage = 'NFCが利用できません。設定を確認してください。';
});
return;
}
setState(() {
isReading = true;
errorMessage = '';
});
try {
debugLog('Starting NFC Session...');
await NfcManager.instance.startSession(
pollingOptions: {
NfcPollingOption.iso14443,
NfcPollingOption.iso15693,
NfcPollingOption.iso18092,
},
alertMessage: 'NFCカードを近づけてください',
onDiscovered: (NfcTag tag) async {
try {
debugLog('Tag detected: ${tag.data}');
// 利用可能な技術をチェック
debugLog('Available technologies:');
if (NfcA.from(tag) != null) debugLog('- NFC-A available');
if (NfcB.from(tag) != null) debugLog('- NFC-B available');
if (NfcF.from(tag) != null) debugLog('- NFC-F available');
if (NfcV.from(tag) != null) debugLog('- NFC-V available');
if (IsoDep.from(tag) != null) debugLog('- ISO-DEP available');
if (Ndef.from(tag) != null) debugLog('- NDEF available');
// FeliCa
if (NfcF.from(tag) != null) {
final nfcF = NfcF.from(tag);
debugLog('FeliCa detected: ${nfcF?.identifier}');
final idm = nfcF!.identifier;
final idmString = idm.map((e) => e.toRadixString(16).padLeft(2, '0')).join(':');
setState(() {
cardId = 'FeliCa: $idmString';
});
}
await NfcManager.instance.stopSession();
setState(() {
isReading = false;
});
} catch (e, stackTrace) {
debugLog('Card Reading Error: $e');
debugLog('Stack trace: $stackTrace');
setState(() {
errorMessage = 'カード読み取りエラー: $e';
isReading = false;
});
await NfcManager.instance.stopSession();
}
},
onError: (error) async {
// エラーの詳細情報を出力
debugLog('NFC Error Type: ${error.runtimeType}');
debugLog('NFC Error ToString: ${error.toString()}');
if (error is PlatformException) {
debugLog('Platform Error:');
debugLog(' Message: ${error.message}');
debugLog(' Details: ${error.details}');
}
setState(() {
errorMessage = 'NFCエラー: ${error.toString()}';
isReading = false;
});
try {
await NfcManager.instance.stopSession();
} catch (e) {
debugLog('Stop session error: $e');
}
},
);
} catch (e, stackTrace) {
debugLog('Start Session Error: $e');
debugLog('Stack trace: $stackTrace');
setState(() {
errorMessage = 'NFCセッション開始エラー: $e';
isReading = false;
});
try {
await NfcManager.instance.stopSession();
} catch (e) {
debugLog('Stop session error: $e');
}
}
}
// デバッグログ関数の改善
// void debugLog(String message) {
// print(message); // コンソールログ
// setState(() {
// final timestamp = DateTime.now().toString().split('.').first;
// debugInfo = '$debugInfo[$timestamp] $message\n';
// });
// }
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('NFC Reader'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _checkNfcAvailability,
),
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!isAvailable)
Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: Colors.red.shade100,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'NFCは利用できません。\nデバイスがNFCに対応しているか確認してください。',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.red),
),
)
else ...[
Icon(
isReading ? Icons.nfc : Icons.nfc_outlined,
size: 100,
color: isReading ? Colors.blue : Colors.grey,
),
const SizedBox(height: 20),
if (isReading)
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: const Column(
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(
'NFCカードを近づけてください',
style: TextStyle(fontSize: 18),
),
],
),
),
if (cardId.isNotEmpty) ...[
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
const Text(
'カードID',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
cardId,
style: const TextStyle(fontSize: 18),
),
],
),
),
],
if (errorMessage.isNotEmpty) ...[
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Text(
errorMessage,
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
),
],
const SizedBox(height: 40),
ElevatedButton.icon(
onPressed: isReading ? null : startNfcReading,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 16,
),
),
icon: const Icon(Icons.nfc),
label: Text(
isReading ? '読み取り中...' : 'NFCを読み取る',
style: const TextStyle(fontSize: 18),
),
),
const SizedBox(height: 20),
if (debugInfo.isNotEmpty) ...[
const Divider(),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'デバッグ情報:',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(debugInfo, style: const TextStyle(fontFamily: 'monospace')),
],
),
),
],
],
],
),
),
),
);
}
void dispose() {
try {
NfcManager.instance.stopSession();
} catch (e) {
print('Error stopping NFC session: $e');
}
super.dispose();
}
}