1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flutter nfc_manager パッケージの使い方

1
Last updated at Posted at 2026-05-23

TL;DR;

nfc_managerに破壊的変更が入ったことで、インターネットにある書き方と、公式ドキュメントの書き方が変わってしまっています。
これが問題なので、まずは読み取りにおける手法に関して、新しい書き方でまとめていきます。

公式ドキュメント

作ったもの

nfc_helper.dart
// Dart imports:
import 'dart:async';

// Package imports:
import 'package:logger/logger.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/nfc_manager_android.dart';

// Project imports:
import './nfc_data.dart';
import './repo_nfc.dart';

class ImplNfc implements RepoNfc {
  ImplNfc({required NfcManager manager, required Logger logger})
    : _manager = manager,
      _logger = logger;

  final NfcManager _manager;
  final Logger _logger;
  Completer<NfcData?> completer = Completer<NfcData?>();

  @override
  Future<NfcData?> read() async {
    completer = Completer<NfcData?>();

    if (await isAvailable) {
      _logger.i('NFC is available');

      await _manager.startSession(
        pollingOptions: {NfcPollingOption.iso18092, NfcPollingOption.iso14443},
        onDiscovered: (NfcTag tag) async {
          try {
            // Try NfcA first (most common)
            final NfcAAndroid? nfcA = NfcAAndroid.from(tag);
            if (nfcA != null) {
              final idm = nfcA.tag.id.toString();
              final nfcData = NfcData(type: NfcType.nfcA, idm: idm);
              if (!completer.isCompleted) completer.complete(nfcData);
              await _manager.stopSession();
              return;
            }

            // Try NfcB as fallback
            final NfcBAndroid? nfcB = NfcBAndroid.from(tag);
            if (nfcB != null) {
              final idm = nfcB.tag.id.toString();
              final nfcData = NfcData(type: NfcType.nfcB, idm: idm);
              if (!completer.isCompleted) completer.complete(nfcData);
              await _manager.stopSession();
              return;
            }

            // Try NfcF as last resort
            final NfcFAndroid? nfcF = NfcFAndroid.from(tag);
            if (nfcF != null) {
              _logger.i('NfcF tag detected');
              final idm = nfcF.tag.id.toString();
              final nfcData = NfcData(type: NfcType.nfcF, idm: idm);
              if (!completer.isCompleted) completer.complete(nfcData);
              await _manager.stopSession();
              return;
            }

            throw Exception('Unsupported tag type');
          } catch (e, st) {
            _logger.e(
              'Error occurred while reading NFC tag',
              error: e,
              stackTrace: st,
            );
            if (!completer.isCompleted) completer.complete(null);
            await _manager.stopSession();
          }
        },
      );
    } else {
      _logger.i('NFC is not available');
      completer.complete(null);
    }

    return completer.future;
  }

  @override
  Future<bool> get isAvailable async {
    return await _manager.checkAvailability() == NfcAvailability.enabled;
  }

  @override
  Future<void> cancel() async {
    await _manager.stopSession();
    completer.complete(null);
  }
}

主に変わったところ

  • NfcAなどの種類において、AndoroidとIOSで使用型が変わった
  • Ndefなどを利用したい場合は、nfc_manager_ndefを別途importしないといけなくなった
  • NfcManager.instanceisAvailableがなくなった
    • 代わりに、Availabilityメソッドが追加された
  • NfcTag自体のレコードの扱い方がガッツリ変わった
    • idmなどを直接取得できなくなった(Unit8List型になった)

NFC Managerの注意点

  • NfcManager.instance自体はシングルトンではないため、別途状態管理する必要がある
  • NfcManager.instance.startSession()はNative側のI/Oポートを開く処理をするだけなので、この処理は読み込む処理自体ではない
    • そのため、startSessionの直後に、見つかった後の処理を書いてはいけない(特にstopSessionを含む処理)
  • また、Helper関数を作る際は、completer を使うべき
    • これは、startSessionがIO開くだけで、見つかるまで待ってくれないので、Completerで強制的に完了を待たせるようにするべき。
    • すると使用感が、普通の非同期実行のread関数になる

あと、型の考え方として、

NfcTag -> NfcAAndoroidやNdefMessageなどの具体型

という流れがある。
NfcTagにはバイナリ形式のRecordが入っているだけなので、これを具体型に変換して使用するというのが基本的な流れ。
この変換はNullableで行われるため、変換後 Null になれば、Recordはその具体型ではない、みたいな分岐処理が書ける。

ただし、Nullable なので、Nullチェックを必ず挟まないといけない(コンパイルエラーになる)。

終わりに

備忘録なので、わかりにくかったらごめんなさい。
Completerの処理に迷っているので、他にいい方法がある方はぜひ、コメントください!

(具体的には、外部からのCancelやタイムアウト処理に迷っています)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?