Open3

Flutter NFC

JboyHashimotoJboyHashimoto

NFCカードをスキャンしたい

こちらのパッケージを使用するとできるようだ?
https://pub.dev/packages/nfc_manager

iOSは設定が大変

x-codeでこちらを追加しておく。
AppleDeveloperのアカウントが必要だった。

Apple DeveloperのサイトでNFCの設定をしておく。








JboyHashimotoJboyHashimoto

Androidの設定はこれだけ

<manifest>
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" android:required="true" />
</manifest>
JboyHashimotoJboyHashimoto

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();
  }
}