前編(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で記述) |
使用パッケージ |
|
使用端末 | 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点です。
iOS/Androidの両対応で、読み取り・書き込みがどちらも動いた
他のライブラリだとiOS側のNDEF書き込みが未対応なものが多いですが、flutter_nfc_kitは比較的安定していました。APIの構造がシンプルで扱いやすい
poll()→readNDEFRecords()→writeNDEFRecords()の流れがわかりやすく、FlutterFlowのCustom Actionにも素直に組み込めました。
Streamやイベントリスナー形式ではないので、非同期の扱いが少し楽です。メンテナンスが比較的活発
リポジトリの更新が止まっていない点と、最近の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.entitlements に com.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トリガーを自然に取り入れることができそうです。
