Flutter NFC Wallet Suppression Plugin

pub package Pub Points Flutter Platform Supported Platforms

A lightweight Flutter plugin that suppresses NFC wallet presentation — preventing Apple Wallet, Google Wallet, and other payment apps from automatically popping up when your NFC-enabled device detects contactless payment terminals or NFC tags.

🎯 Problem This Solves: When users with NFC-enabled phones (iPhone 7+, most modern Android devices) approach an NFC reader while your app is open, the system automatically shows wallet/payment apps. This plugin gives your app exclusive control over NFC interactions instead.

⚠️ Important Notes:

  • Only needed for NFC-enabled devices - Phones without NFC hardware don't have this issue
  • Suppression only - This plugin does NOT read or write NFC tags
  • Best for: Loyalty cards, ticketing, access control, and custom NFC experiences

📋 Table of Contents


Key Features

  • Suppress NFC wallet presentation - Stop Apple Wallet/Google Wallet from auto-appearing on NFC-enabled devices
  • Simple API - Three methods: request, release, and check suppression status
  • Cross-platform - Works on iOS (PassKit) and Android (NFC Adapter)
  • All platforms supported - Graceful fallback on web/desktop (returns notSupported)
  • Lifecycle-aware - Automatic cleanup when app backgrounds
  • Type-safe status - Detailed status enum for handling different scenarios

Build Status

CI

Integration Tests SDK Compatibility Check OpenSSF Scorecard codecov


Installation

flutter pub add nfc_wallet_suppression

Platform Setup

Android

Minimum: Android 5.0+ (API 21)

Setup: None required! NFC permission is automatically added. App must be in foreground when requesting suppression.

Note: Only suppresses wallet on devices with NFC hardware. Non-NFC devices don't need suppression.

iOS

Minimum: iOS 13.0+ (this plugin's floor; the PassKit suppression API is available from iOS 9.0+)

Your app's actual minimum is whichever is higher — this plugin's floor or the one your Flutter version requires. Flutter 3.47, for example, requires iOS 15.0.

Note: Only suppresses wallet on devices with NFC hardware (iPhone 7+). Older iPhones without NFC don't need suppression.

Setup Required:

  1. Request Apple Entitlement

    • Email apple-pay-inquiries@apple.com
    • Request com.apple.developer.passkit.pass-presentation-suppression
    • Explain your use case (may take several days for approval)
  2. Configure Developer Portal

    • Enable Pass Presentation Suppression in your App ID
    • Regenerate provisioning profiles
  3. Add Entitlement File (ios/Runner/Runner.entitlements):

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.developer.passkit.pass-presentation-suppression</key>
        <true/>
    </dict>
    </plist>
    

⚠️ Without the approved entitlement, calls will fail on real iOS devices.


API Usage

Quick Example

import 'package:nfc_wallet_suppression/nfc_wallet_suppression.dart';

// Request NFC wallet suppression
try {
  SuppressionStatus status = await NfcWalletSuppression.requestSuppression();
  
  if (status == SuppressionStatus.suppressed) {
    print('✓ Suppression active - you can now handle NFC interactions');
  } else {
    print('⚠ Suppression status: ${status.name}');
  }
} catch (error) {
  print('Error requesting NFC wallet suppression: $error');
}

// Check if NFC wallet is currently suppressed
try {
  bool isSuppressed = await NfcWalletSuppression.isSuppressed();
  print('Is NFC wallet suppressed? $isSuppressed');
} catch (e) {
  print('Error checking suppression status: $e');
}

// Release NFC wallet suppression when done
try {
  SuppressionStatus status = await NfcWalletSuppression.releaseSuppression();
  print('Suppression released: ${status.name}');
} catch (e) {
  print('Error releasing NFC wallet suppression: $e');
}

API Methods

Method Returns Description
requestSuppression() Future<SuppressionStatus> Request wallet suppression
releaseSuppression() Future<SuppressionStatus> Release suppression
isSuppressed() Future<bool> Check if currently suppressed

SuppressionStatus Values

The SuppressionStatus enum represents the result of suppression operations:

Status Description
suppressed Suppression is active
notSuppressed Suppression is not active (e.g. successfully released)
unavailable No active suppression to release; or, during requestSuppression on Android, NFC is turned off or no activity is attached (recoverable)
denied Permission denied — iOS: by the user or system; Android: a SecurityException on the NFC call
cancelled User cancelled the permission prompt (iOS only)
notSupported Suppression isn't supported — Android: no NFC hardware; iOS: PassKit reports it unsupported
alreadyPresenting Wallet is already presenting a pass (iOS only)
unknown An unexpected error occurred

Important Notes:

  • ✅ Only affects NFC-enabled devices (iPhone 7+, most modern Android phones)
  • ✅ Auto-releases when app backgrounds or closes
  • ❌ Does NOT read/write NFC tags
  • ❌ Does NOT persist across app restarts
  • ⚠️ Best-effort (not guaranteed on all devices due to manufacturer customizations)

Troubleshooting

Common Issues:

Issue Platform Solution
Entitlement not found iOS Ensure Apple approved the entitlement and it's in your provisioning profile
NFC not available Android Enable NFC in device settings
Auto-released Both Use WidgetsBindingObserver to re-request on app resume
Doesn't work on device iOS Test on physical iPhone 7+, verify entitlement in Xcode
Payment apps still appear Android Some manufacturers override behavior, test on multiple devices

FAQ:

  • Does this read NFC tags? No, it only suppresses wallet presentation.
  • How long does suppression last? Until released or app backgrounds.
  • Works in simulator? Limited - always test on physical devices with NFC.

Testing

This plugin includes comprehensive testing utilities. See the TESTING.md guide for:

  • Using FakeNfcWalletSuppression for unit tests
  • Pre-configured test scenarios
  • Widget testing examples
  • Integration testing guidance

Quick example:

import 'package:nfc_wallet_suppression/testing.dart';

final fake = NfcWalletSuppressionTestScenarios.supportedDevice();
NfcWalletSuppressionPlatform.instance = fake;

// Now your tests use the fake implementation

Example App

See the example/ directory for a complete working example with lifecycle management and error handling.

cd example && flutter run

Privacy

This plugin includes a privacy manifest (ios/nfc_wallet_suppression/Sources/nfc_wallet_suppression/PrivacyInfo.xcprivacy) that declares:

  • No tracking (NSPrivacyTracking: false)
  • No data collection (NSPrivacyCollectedDataTypes: empty)
  • No required reason APIs (NSPrivacyAccessedAPITypes: empty)
  • No tracking domains (NSPrivacyTrackingDomains: empty)

The PassKit APIs used for NFC wallet suppression do not access sensitive user data, do not track users, and do not fall under Apple's required reason API categories. The privacy manifest is included as a best practice for App Store compliance.


Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

Additional Resources

API DocsReport IssuesCHANGELOGApple PassKitAndroid NFC


License

BSD 3-Clause License - See LICENSE for details.

Libraries

nfc_wallet_suppression
testing
Testing utilities for nfc_wallet_suppression plugin.