NFC 라이브러리는 nfc_manager를 사용하였습니다.
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:android_intent_plus/android_intent.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/platform_tags.dart';
import 'package:redixplus/auth_util/toast_util.dart';
import '../../const/const.dart';
import '../util.dart';
Future<void> readAndWriteNfc() async {
//nfc 사용이 가능한지 확인
bool isAvailable = await NfcManager.instance.isAvailable();
//nfc 사용이 불가능한 경우
if (!isAvailable) {
if (Platform.isAndroid) {
//android 일 경우 nfc 설정창으로 이동 시킴
//ios는 nfc가 os단에서 감지하고 실행되기 때문에 안드로이드처럼 설정창으로 보낼 수 없다.
const AndroidIntent intent = AndroidIntent(
action: 'android.settings.NFC_SETTINGS',
);
intent.launch();
return;
}
//토스트 띄우기
ToastUtil.show(message: NFC_DISALBE_DEVICE_MESSAGE);
return;
}
await nfcRead();
}
Future<void> nfcRead() async {
NfcManager.instance.startSession(
//nfc 태그를 읽을 수 있는 타입을 지정한다.
//타입에 따라 읽을 수 있는 nfc 태그가 달라진다. (iso14443는 교통카드, iso15693는 물류,재고관리 등에 쓰인다고 한다)
//pollingOptions을 지정하지 않으면 모든 타입의 nfc 태그를 읽을 수 있다.
//특정 태그만 읽어야 하는 기능은 아니라서 아래 줄은 필요 없을 것 같다.
//pollingOptions: {NfcPollingOption.iso14443, NfcPollingOption.iso15693},
//ios에서만 사용되는 옵션
alertMessage: NFC_SCAN_MESSAGE,
onError: (NfcError error) {
//에러 시 세션 멈춤
return NfcManager.instance.stopSession(alertMessage: NFC_SCAN_FAIL);
},
//nfc 태그를 읽었을 때
onDiscovered: (NfcTag tag) async {
//nfc id 가져오기
String id = getNfcId(tag);
//nfc 페이로드 가져오기
String? decodedNfcMessage = getDecodedNfcMessage(tag);
//ios만 세션 스탑 해줌
//ios는 stop 안하면 팝업이 계속 뜸
//android는 stop 하면 nfc가 연속적으로 읽힘
if (isIos) {
NfcManager.instance.stopSession(alertMessage: NFC_SCAN_SUCCESS);
}
//nfc write
await nfcWrite('hello world!', 'https://www.naver.com');
},
);
}
Future<void> nfcWrite(String stringData, String url) async {
if (isIos) {
//ios는 연속적인 nfc scan이 안되서 몇초간 딜레이를 준 후 실행
await Future.delayed(const Duration(milliseconds: 4000), () {});
}
NfcManager.instance.startSession(
//pollingOptions: {NfcPollingOption.iso14443, NfcPollingOption.iso15693},
alertMessage: NFC_SCAN_MESSAGE,
onDiscovered: (NfcTag tag) async {
var ndef = Ndef.from(tag);
//nfc 태그가 쓰기가 가능한지 확인
if (ndef == null || !ndef.isWritable) {
debugPrint('NFC DISABLE WRITE TYPE');
//nfc 태그가 쓰기가 불가능하면 세션 멈춤
NfcManager.instance.stopSession(errorMessage: NFC_SCAN_FAIL);
return;
}
//nfc 태그에 쓸 메시지 생성
NdefMessage message = NdefMessage(
[
NdefRecord.createUri(
Uri.parse(
url,
),
),
NdefRecord.createMime(
'text/plain',
Uint8List.fromList(
stringData.codeUnits,
),
),
],
);
//nfc 태그에 쓰기
await ndef.write(message);
//ios만 세션 스탑 해줌
//ios는 stop 안하면 팝업이 계속 뜸
//android는 stop 하면 nfc가 연속적으로 읽힘
if (isIos) {
NfcManager.instance.stopSession(alertMessage: NFC_SCAN_SUCCESS);
}
},
);
}
String getNfcId(NfcTag tag) {
//ios 일 경우
if (isIos) {
//Mifare는 ios 에서 사용되는 NFCMiFareTag 클래스에 접근하게 해줌
var mifare = MiFare.from(tag);
return mifare!.identifier
.map((e) => e.toRadixString(16).padLeft(2, '0'))
.join('');
} else {
//android 일 경우
Ndef? ndef = Ndef.from(tag);
return ndef?.additionalData['identifier']
.map((e) => e.toRadixString(16).padLeft(2, '0'))
.join('');
}
}
//nfc 페이로드 가져오기
String? getDecodedNfcMessage(NfcTag tag) {
//ndef = NFC 데이터 교환에 이용되는 데이터 교환 포맷
Ndef? ndef = Ndef.from(tag);
NdefMessage? nfcMessage = ndef?.cachedMessage;
//ndef message > ndef record > header(record에 대한 기본정보) + payload(첫 바이트는 페이로드의 헤더, 나머지는 페이로드 데이터)
//페이로드의 타입이 text/plain 인 것을 찾는다
NdefRecord? textTypeData = nfcMessage?.records
.firstWhereOrNull((e) => utf8.decode(e.type) == 'text/plain');
if (textTypeData != null) {
Uint8List uint8ListData = Uint8List.fromList(textTypeData.payload);
//페이로드의 첫 바이트는 페이로드의 헤더이므로 제외하고 utf8로 디코딩한다.
return utf8.decode(uint8ListData.sublist(1));
}
return null;
}