nfc_util 2.0.0
nfc_util: ^2.0.0 copied to clipboard
A Flutter plugin providing access to NFC features on Android and iOS.
example/lib/main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:nfc_util/nfc_util.dart';
import 'package:nfc_util/platform_tags.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<StatefulWidget> createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
ValueNotifier<dynamic> result = ValueNotifier(null);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Nfc Util Plugin Example')),
body: SafeArea(
child: FutureBuilder<bool>(
future: NfcManager.instance.isAvailable(),
builder: (context, ss) => ss.data != true
? Center(child: Text('NfcUtil.isAvailable(): ${ss.data}'))
: Flex(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
direction: Axis.vertical,
children: [
Flexible(
flex: 2,
child: Container(
margin: EdgeInsets.all(4),
constraints: BoxConstraints.expand(),
decoration: BoxDecoration(border: Border.all()),
child: SingleChildScrollView(
child: ValueListenableBuilder<dynamic>(
valueListenable: result,
builder: (context, value, _) => Text('${value ?? ''}'),
),
),
),
),
Flexible(
flex: 3,
child: GridView.count(
padding: EdgeInsets.all(4),
crossAxisCount: 2,
childAspectRatio: 4,
crossAxisSpacing: 4,
mainAxisSpacing: 4,
children: [
ElevatedButton(onPressed: _tagRead, child: Text('Tag Read')),
ElevatedButton(onPressed: _ndefWrite, child: Text('Ndef Write')),
ElevatedButton(onPressed: _ndefWriteLock, child: Text('Ndef Write Lock')),
ElevatedButton(onPressed: _tagIo, child: Text('Tag I/O')),
],
),
),
],
),
),
),
),
);
}
/// The tag's UID as uppercase hex. Every technology map carries it under `identifier`;
/// FeliCa reports it as `currentIDm` instead.
static String _tagCode(NfcTag tag) {
for (final tech in tag.data.values) {
if (tech is! Map) continue;
final id = tech['identifier'] ?? tech['currentIDm'];
if (id is List<int> && id.isNotEmpty) {
return id.map((e) => e.toRadixString(16).padLeft(2, '0').toUpperCase()).join(':');
}
}
return '(no identifier)';
}
/// iOS reports a session that could not start (or was cancelled, or timed out) here
/// rather than by failing `startSession`. Without this callback those failures are
/// completely silent.
Future<void> _onSessionError(NfcError error) async {
final line = 'session error: ${error.type.name}: ${error.message}';
debugPrint('NFCTEST $line');
result.value = line;
}
void _tagRead() {
unawaited(
NfcManager.instance.startSession(
pollingOptions: {NfcPollingOption.iso14443},
onError: _onSessionError,
onDiscovered: (NfcTag tag) async {
result.value = 'UID: ${_tagCode(tag)}\n\n${tag.data}';
await NfcManager.instance.stopSession();
},
),
);
}
void _ndefWrite() {
unawaited(
NfcManager.instance.startSession(
onError: _onSessionError,
onDiscovered: (NfcTag tag) async {
final ndef = Ndef.from(tag);
if (ndef == null || !ndef.isWritable) {
result.value = 'Tag is not ndef writable';
await NfcManager.instance.stopSession(errorMessage: 'Tag is not ndef writable');
return;
}
final message = NdefMessage([
NdefRecord.createText('Hello World!'),
NdefRecord.createUri(Uri.parse('https://flutter.dev')),
NdefRecord.createMime('text/plain', Uint8List.fromList('Hello'.codeUnits)),
NdefRecord.createExternal('com.example', 'mytype', Uint8List.fromList('mydata'.codeUnits)),
]);
try {
await ndef.write(message);
result.value = 'Success to "Ndef Write"';
await NfcManager.instance.stopSession();
} on Object catch (e) {
result.value = e;
await NfcManager.instance.stopSession(errorMessage: e.toString());
}
},
),
);
}
/// Runs read-only commands against whichever technologies the tag supports, so the
/// platform's tag I/O path is exercised end to end. Nothing here writes to the tag.
void _tagIo() {
unawaited(
NfcManager.instance.startSession(
onError: _onSessionError,
onDiscovered: (NfcTag tag) async {
final started = DateTime.now();
final log = StringBuffer('UID: ${_tagCode(tag)}\n\n');
Future<void> step(String label, Future<Object?> Function() body) async {
final at = DateTime.now().difference(started).inMilliseconds;
String line;
try {
line = '${at}ms $label -> ${await body()}';
} on Object catch (e) {
final code = e is PlatformException ? '${e.code}: ${e.message}' : e.runtimeType.toString();
line = '${at}ms $label !! $code';
}
log.writeln(line);
debugPrint('NFCTEST $line');
result.value = log.toString();
}
// Interleaved on purpose: if only the first call fails the problem is timing,
// but if a given technology fails every time it is reached the problem is the
// connect/close handling when switching technologies.
final ndef = Ndef.from(tag);
final nfcA = NfcA.from(tag);
final ultralight = MifareUltralight.from(tag);
final isoDep = IsoDep.from(tag);
final miFare = MiFare.from(tag);
for (var round = 1; round <= 2; round++) {
if (ndef != null) {
await step('[$round] Ndef.read()', () async => (await ndef.read())?.records.length);
}
if (nfcA != null) {
// NTAG/Ultralight READ of page 0; harmless on other NfcA tags.
await step('[$round] NfcA.transceive', () => nfcA.transceive(data: Uint8List.fromList([0x30, 0x00])));
}
if (ultralight != null) {
await step('[$round] MifareUltralight.readPages', () => ultralight.readPages(pageOffset: 0));
}
if (isoDep != null) {
// SELECT by name, empty AID: every ISO-DEP card answers something.
await step('[$round] IsoDep.transceive', () => isoDep.transceive(data: Uint8List.fromList([0x00, 0xA4, 0x04, 0x00, 0x00])));
}
if (miFare != null) {
await step('[$round] MiFare.sendMiFareCommand', () => miFare.sendMiFareCommand(Uint8List.fromList([0x30, 0x00])));
}
}
if (ndef == null) log.writeln('Ndef.from(tag) -> null (tag is not NDEF formatted)');
log.writeln('done, keep the tag still until this line appears');
result.value = log.toString();
await NfcManager.instance.stopSession();
},
),
);
}
void _ndefWriteLock() {
unawaited(
NfcManager.instance.startSession(
onError: _onSessionError,
onDiscovered: (NfcTag tag) async {
final ndef = Ndef.from(tag);
if (ndef == null) {
result.value = 'Tag is not ndef';
await NfcManager.instance.stopSession(errorMessage: 'Tag is not ndef');
return;
}
try {
await ndef.writeLock();
result.value = 'Success to "Ndef Write Lock"';
await NfcManager.instance.stopSession();
} on Object catch (e) {
result.value = e;
await NfcManager.instance.stopSession(errorMessage: e.toString());
}
},
),
);
}
}