Stack:
- Flutter
- Pakete: nfc_manager, nfc_manager_ndef, ndef_record
- Geräte: Samsung SM-S918B (One UI/Android 14) oder Samsung Galaxy
Note10+
- Native ReaderMode (Kotlin) mit „Sticky“-Aktivierung
Code: Select all
class MainActivity : FlutterActivity() {
private val CHANNEL = "de.mytagapp.app/nfc"
private var nfcAdapter: NfcAdapter? = null
private var readerModeSticky = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"enableReaderMode" -> {
readerModeSticky = true
enableReaderModeInternal()
result.success(true)
}
"disableReaderMode" -> {
readerModeSticky = false
disableReaderModeInternal()
result.success(true)
}
else -> result.notImplemented()
}
}
}
override fun onResume() {
super.onResume()
if (readerModeSticky) enableReaderModeInternal()
}
override fun onPause() {
super.onPause()
// disable when not visible
disableReaderModeInternal()
}
private fun enableReaderModeInternal() {
val flags =
NfcAdapter.FLAG_READER_NFC_A or
// NfcAdapter.FLAG_READER_NFC_V // (tested on/off)
NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS or
NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
// No-op callback; Flutter handles reads/writes.
nfcAdapter?.enableReaderMode(this, { /* no-op */ }, flags, null)
}
private fun disableReaderModeInternal() {
nfcAdapter?.disableReaderMode(this)
}
}
Aktivität hat launchMode="singleTop".
- Globaler „Schutz“ auf der Flutter-Seite
Code: Select all
class NfcReaderGuard extends StatefulWidget {
final Widget child;
const NfcReaderGuard({super.key, required this.child});
@override
State createState() => _NfcReaderGuardState();
}
class _NfcReaderGuardState extends State with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
NfcReaderMode.enable(); // MethodChannel -> enableReaderMode
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) NfcReaderMode.enable();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
NfcReaderMode.disable();
super.dispose();
}
@override
Widget build(BuildContext context) => widget.child;
}
- Nur vom Benutzer ausgelöste Sitzungen
- Ich rufe NfcManager.instance.startSession(...) nur auf, nachdem der Benutzer auf
eine Schaltfläche (Lesen/Schreiben) tippt, und stoppe es direkt danach. - Ich überprüfe zuerst die Verfügbarkeit.
- Datensätze sind RTD_TEXT; Keine URL-Einträge, die das System dazu einladen würden,
einen Browser zu öffnen.
Trotz alledem verarbeitet das System auf dem Samsung-Gerät das Tag manchmal immer noch automatisch (als ob mein ReaderMode es nicht „besitzen“ würde). Selbst das Fokussieren der App (Tippen auf die Benutzeroberfläche) verhindert nicht immer, dass der automatische Lese-/Handler angezeigt wird. Ich sehe das am häufigsten, wenn die App den Fokus wiedererlangt oder nach einem kurzen Aus-/Ein-Zyklus des Bildschirms.
Fragen an die Samsung-/NFC-/Android-/Flutter-Experten:
- Überschreibt Samsung das ReaderMode-Verhalten? Auf AOSP-Geräten würde ich
enableReaderMode(...) mit SKIP_NDEF_CHECK erwarten, um
die NDEF-Prüfung/automatische Benutzeroberfläche der Plattform zu stoppen. Gibt es Samsung-spezifische Macken
wo das System immer noch an seinen eigenen Handler sendet? - Schieße ich mir selbst ins Bein, indem ich enableReaderMode(...)
und nfc_manager.startSession(...) kombiniere? Könnte nfc_manager intern
seinen eigenen Reader/Vordergrund-Versand umschalten und versehentlich
Systemstandards auf Samsung wieder aktivieren? - Würden Sie empfehlen, nfc_manager.startSession überhaupt nicht zu verwenden und
stattdessen alle Tag-Erkennung im nativen onTagDiscovered durchzuführen und dann
Bytes über MethodChannel an Flutter weiterzuleiten? - Wäre „enableForegroundDispatch(...)“ auf Samsung zuverlässiger als
ReaderMode hier? Wenn ja, gibt es eine minimale Einrichtung, die Absichten in meiner
Aktivität aufrechterhält und zuverlässig verhindert, dass Samsung Wallet/Tag-Apps
übernehmen – ohne dass Benutzer die Geräteeinstellungen ändern müssen? - Irgendwelche Samsung-spezifischen Flags oder Best Practices, die mir fehlen?
- Zusätzliche Reader-Flags?
- Timing-Bedenken (z. B. Wiederaktivierung zu einem späteren Zeitpunkt). Lifecycle-Callback)?
- Bekannte One UI-Verhaltensweisen, die einen anderen Ansatz erfordern? - Andere Problemumgehungen, die Sie in der Produktion verwendet haben? Meine Einschränkungen:
- Ich muss System-Popups/automatisches Öffnen vermeiden.
- Lese-/Schreibvorgänge sollten nur bei expliziter Benutzeraktion erfolgen.
- Ich würde Benutzer lieber nicht bitten, Samsung Wallet/„Tag“-Dienste
in den Einstellungen zu deaktivieren, da dies das Tag-Lesen generell deaktivieren würde (wie
meine Tests zeigen)
- Für die Entdeckung vollständig nativ gehen: onTagDiscovered in Kotlin verarbeiten, NDEF dort analysieren (oder einfach rohe Technologie/Bytes übergeben) an Flutter; Rufen Sie
nfc_manager.startSession überhaupt nicht auf. - Versuchen Sie Foreground Dispatch anstelle von ReaderMode und stellen Sie sicher, dass keine Filter
URL-Verarbeitung zulassen. - Drosseln Sie das Timing für onResume() erneut (z. B. eine kleine Verzögerung veröffentlichen), falls Samsung während des Lebenslaufs etwas unternimmt.
- Überprüfen Sie die zum Testen verwendeten Tags noch einmal (stellen Sie sicher, dass sie keine
URLs/Datensätze enthalten, mit denen Samsung aggressiv umgeht; meine sind nur RTD_TEXT
, aber ich teste noch einmal mit empty/FORMAT).
Mobile version