前編(NFCを活用したアプリ開発【前編】仕組みと活用例)ではNFCの仕組みやタグの種類、活用例を整理しました。

後編は、実際にFlutterFlow + Custom Action(Dart)で「NFCの読み取り・書き込み」を行うミニアプリを作った記録と、手順・注意点のまとめです。結論から言うと、FlutterFlowでもNFCは動きます。ただし、いくつかの罠があります。

FlutterFlowはノーコードツールの中でもかなり柔軟ですが、NFCのようなハードウェア寄りの機能は標準では扱えないものも多いです。
だから今回は、FlutterFlowのUI設計をベースにしつつ、Dartで書いたCustom Actionを組み込む形でアプリを構築しました。
両者のいいとこ取りをしながら、実際に動くところまで持っていくまでの流れを、順を追って紹介します。

1. ゴールと前提

  • ゴール

    タグのメタ情報取得

    NDEFの読み取り

    テキストの書き込み(Androidのみ)

  • 想定タグ: NTAG213/215/216(NDEF対応)

  • 対応状況

    Android: 読み取り/書き込み

    iOS: 読み取り/書き込み(バッググラウンドは不可)

2. 実験環境

項目

内容

使用ツール

FlutterFlow(Blankプロジェクト)

開発言語

Dart(Custom Actionで記述)

使用パッケージ

flutter_nfc_kit: ^3.6.0, ndef: ^0.3.4

使用端末

Pixel 9 (Android 14), iPhone 13 (iOS 18)

NFCタグ

NTAG215(NDEF対応)

3. アプリ構成(ミニマル)

UIはとてもシンプルです。1ページにボタンを3つ置きました。

  • メタ情報をスキャン

  • NDEFを読み取る

  • テキストを書き込む(入力欄付き)

HomePage(UI)
 ├─ Button: メタ情報スキャン → Custom Action: scanNfcMeta
 ├─ TextField + Button: 書き込み → Custom Action: writeTextToNfc
 ├─ Button: NDEF読み取り     → Custom Action: readNfcNdef
 └─ 下部に結果表示(nfcMetaJson / nfcNdefJson)

状態は FFAppState に文字列で保持(nfcMetaJson, nfcNdefJson, nfcWrittenText)。

4. 実装ステップ

1) 依存パッケージを追加(FlutterFlow → Settings → Pubspec)

NFC関連のFlutterパッケージはいくつかあります。
nfc_manager, nfc_in_flutter, flutter_nfc_reader などがよく使われていますが、今回試した範囲では、flutter_nfc_kit: ^3.6.0 が一番バランスが良い印象でした。

特に感じたのはこの3点です。

  1. iOS/Androidの両対応で、読み取り・書き込みがどちらも動いた
    他のライブラリだとiOS側のNDEF書き込みが未対応なものが多いですが、flutter_nfc_kit は比較的安定していました。

  2. APIの構造がシンプルで扱いやすい
    poll()readNDEFRecords()writeNDEFRecords() の流れがわかりやすく、FlutterFlowのCustom Actionにも素直に組み込めました。
    Streamやイベントリスナー形式ではないので、非同期の扱いが少し楽です。

  3. メンテナンスが比較的活発
    リポジトリの更新が止まっていない点と、最近のAndroid/iOSバージョンでも動いた点が安心材料でした。

もちろん、環境や端末によって結果が違う可能性もありますが、
FlutterFlowのように「ノーコードに近い環境から直接呼び出す」前提だと、flutter_nfc_kit が一番扱いやすい印象でした。

dependencies:
  flutter_nfc_kit: ^3.6.0
  ndef: ^0.3.4

2) App State フィールドを作成

書き込む内容や読み取った内容を保存するためのものです。Custom Codeからも簡単にアクセスできるので重宝します。

  • nfcMetaJson(JSON)

  • nfcWrittenText(String )

  • nfcNdefJson(JSON)

3) Custom Action を3つ作成

3-1. タグのメタ情報取得 scanNfcMeta

import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';

Future<void> scanNfcMeta() async {
  var appState = FFAppState();

  try {
    // NFCチェック
    var availability = await FlutterNfcKit.nfcAvailability;
    if (availability != NFCAvailability.available) {
      throw Exception('NFCが利用できません');
    }

    // タグをスキャン
    var tag = await FlutterNfcKit.poll(iosAlertMessage: 'タグをかざしてください');

    // メタデータをJSON化
    final tagJson = jsonEncode(tag.toJson());

    // AppStateに保存
    appState.update(() {
      appState.nfcMetaJson = tagJson;
    });
  } finally {
    await FlutterNfcKit.finish(iosAlertMessage: 'スキャン完了');
  }
}

3-2. テキスト書き込み writeTextToNfc

import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:ndef/ndef.dart' as ndef;

Future<void> writeTextToNfc(String text) async {
  var appState = FFAppState();

  try {
    print('NFCチェック開始');
    var availability = await FlutterNfcKit.nfcAvailability;
    print('NFC利用可: $availability');

    if (availability != NFCAvailability.available) {
      throw Exception('NFCが利用できません');
    }

    print('タグスキャン開始...');
    var tag = await FlutterNfcKit.poll(
      timeout: Duration(seconds: 10),
      iosAlertMessage: 'タグをかざしてください',
    );
    print('タグ検出: ${jsonEncode(tag)}');

    print('書き込み開始: $text');
    var textRecord = ndef.TextRecord(text: text, language: 'en');
    await FlutterNfcKit.writeNDEFRecords([textRecord]);
    print('書き込み成功!');

    await FlutterNfcKit.finish(iosAlertMessage: '書き込み完了');
    print('セッション終了');
  } catch (e) {
    print('エラー発生: $e');
  }
}

3-3. NDEF読み取り readNfcNdef

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';

Future<void> readNfcNdef() async {
  var appState = FFAppState();

  try {
    // NFC利用可チェック
    var availability = await FlutterNfcKit.nfcAvailability;
    if (availability != NFCAvailability.available) {
      throw Exception('NFCが利用できません');
    }

    // タグスキャン開始
    await FlutterNfcKit.poll(iosAlertMessage: 'タグをかざしてください');

    // NDEFレコード読み取り
    var records = await FlutterNfcKit.readNDEFRecords();

    // ▼ ヘルパー関数:Text Recordのデコード
    String decodeTextPayload(Uint8List payload) {
      if (payload.isEmpty) return '';
      int statusByte = payload[0];
      int langCodeLen = statusByte & 0x3F; // 下位6ビットが言語コード長
      return utf8.decode(payload.sublist(1 + langCodeLen)); // 言語コードをスキップ
    }

    // ▼ JSON構築
    var ndefList = records.map((r) {
      String decodedText = '';
      if (r.payload != null && r.payload!.isNotEmpty) {
        decodedText = decodeTextPayload(r.payload!);
      }
      return {
        'type': r.type, // レコードのタイプ
        'decodedText': decodedText, // 実際のテキスト
        'rawPayload': r.payload != null
            ? base64Encode(r.payload!) // 元データはbase64で保持
            : ''
      };
    }).toList();

    String ndefJson = jsonEncode(ndefList);

    // AppStateに保存
    appState.update(() {
      appState.nfcNdefJson = ndefJson;
    });
  } finally {
    await FlutterNfcKit.finish(iosAlertMessage: '読み取り完了');
  }
}

4) UI にアクションを配線

  • 「メタ情報スキャン」→ scanNfcMeta 成功時: FFAppState().nfcMetaJson = result.resultJson

  • 「NDEF読み取り」→ readNfcNdef 成功時: FFAppState().nfcNdefJson = result.resultJson

  • 「書き込み」→ writeTextToNfc(text: TextField.value) 成功時: FFAppState().nfcWrittenText = TextField.value

5. プラットフォーム設定(ここ大事)

Android(AndroidManifest.xml

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

任意で、NDEFのMIMEを関連付けてアプリを起動したい場合:

<intent-filter>
  <action android:name="android.nfc.action.NDEF_DISCOVERED" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:mimeType="text/plain" />
</intent-filter>

iOS(Info.plist と Capabilities)

<key>NFCReaderUsageDescription</key>
<string>NFCタグを読み取るために使用します。</string>

さらに Xcode → Signing & Capabilities
Near Field Communication Tag Reading を追加(これを忘れると一生読めません)。
Runner.entitlementscom.apple.developer.nfc.readersession.formats が入っていることを確認。

6. 実機での検証結果

操作

Pixel 9 (Android 14)

iPhone 13 (iOS 18)

タグ検出(poll)

メタ情報取得

NDEF読み取り

テキスト書き込み

ロック済タグへの書き込み

特にiPhoneは安定して書き込める印象。
ただし「読み込み完了→タグを離す→すぐ再度書き込み」はセッションの扱いが微妙で、再試行が必要でした。

7. 今後やりたいこと

  • 書き込み前に「このタグ書ける?」を自動チェック

  • タグを読み込んだらアプリを自動起動(Deep Link連携)

  • NFCタグ × IoT連携(例:照明・ToDo起動・出席チェックなど)

8. まとめ

結論として、FlutterFlowでも、Custom Actionを併用すればNFCは十分実用レベルで扱えます。

最大の課題はプラットフォームの制約と端末差です。設計の段階で対応範囲をきちんと整理した上で、UI上のユーザーのサポートと実機での検証を繰り返していけば、安定した体験に近づけます。

「かざす」という物理的な行為がアプリの起点になるのはとても面白く、どんなアプリが作れるかを考えるとワクワクします。日常の中に小さな自動化やチェックイン、IoTトリガーを自然に取り入れることができそうです。