nfc_util 2.0.0 copy "nfc_util: ^2.0.0" to clipboard
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());
          }
        },
      ),
    );
  }
}
2
likes
0
points
388
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin providing access to NFC features on Android and iOS.

Repository (GitHub)
View/report issues

Topics

#nfc #ndef #mifare #felica

License

unknown (license)

Dependencies

flutter

More

Packages that depend on nfc_util

Packages that implement nfc_util