deepidsdk_flutter 1.0.2 copy "deepidsdk_flutter: ^1.0.2" to clipboard
deepidsdk_flutter: ^1.0.2 copied to clipboard

Flutter plugin for the DeepID SDK (Android + iOS). Provides SIM binding, device enrollment, and fraud detection capabilities.

deepidsdk_flutter #

Flutter plugin that bridges the DeepID SDK on Android and iOS.
Provides device enrollment (SIM-based fingerprinting) and an interactive SIM binding flow — a two-step fraud-prevention mechanism that cryptographically links a user's phone number to their device.

Note — SDK binaries are not bundled in this package.
The native Android AAR and iOS xcframework are distributed separately by DeepID. You will receive them via email or by some other means. Place them at the exact paths shown in Prerequisites before running flutter pub get or building your app.


Table of contents #

  1. Prerequisites
  2. How it works
  3. Requirements
  4. Installation
  5. Android setup
  6. iOS setup
  7. Integration walkthrough
  8. API reference
  9. Error handling
  10. Platform differences
  11. Troubleshooting

Prerequisites #

Before adding this plugin to your app you must obtain the native SDK binaries from DeepID (contact deepidsdk.com if you haven't received them yet) and place them inside the plugin's directory in your pub cache.

The pub cache path for this plugin is:

~/.pub-cache/hosted/pub.flutter-io.cn/deepidsdk_flutter-<version>/

Replace <version> with the version you added to your pubspec.yaml (e.g. deepidsdk_flutter-1.0.0).

Android #

~/.pub-cache/hosted/pub.flutter-io.cn/deepidsdk_flutter-<version>/
└── android/
    └── libs/
        └── deepidsdk.aar   ← place the AAR here

The Gradle build script auto-publishes this AAR into a local Maven repository at configuration time. If the file is missing the build will fail immediately with a descriptive error message pointing to this path.

iOS #

~/.pub-cache/hosted/pub.flutter-io.cn/deepidsdk_flutter-<version>/
└── ios/
    └── Frameworks/
        ├── DeepIdSDK.xcframework/       ← place the xcframework here
        │   ├── Info.plist
        │   ├── ios-arm64/
        │   └── ios-arm64_x86_64-simulator/
        └── ShieldPtr.xcframework/       ← place the xcframework here
            ├── Info.plist
            ├── ios-arm64/
            └── ios-arm64_x86_64-simulator/

CocoaPods picks up both xcframeworks via the plugin's podspec. If either directory is absent, pod install will fail with "vendored framework not found".

Verify before continuing #

After placing both files, your pub cache directory should look like this:

~/.pub-cache/hosted/pub.flutter-io.cn/deepidsdk_flutter-<version>/
├── android/
│   └── libs/
│       └── deepidsdk.aar               ✓
├── ios/
│   └── Frameworks/
│       ├── DeepIdSDK.xcframework/      ✓
│       └── ShieldPtr.xcframework/      ✓
├── lib/
└── pubspec.yaml

Only once both binaries are in place should you proceed with the steps below.


How it works #

There are two distinct lifecycle stages:

┌──────────────────────────────────────────────────────────────────────┐
│  Stage 1 — Enrollment (happens automatically after initialize())     │
│                                                                      │
│  App starts  →  DeepId.initialize()  →  SDK contacts DeepID servers  │
│                                         and obtains:                 │
│                                           • deepId   (device token)  │
│                                           • sessionId                │
│                                         Fires onEnrollment callback  │
└──────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────┐
│  Stage 2 — SIM Binding (user-initiated, requires Stage 1 complete)   │
│                                                                      │
│  User taps CTA  →  DeepId.startSimBinding()  →  Native sheet opens   │
│                                                  User selects SIM    │
│                                                  Silent SMS sent     │
│                                                  Carrier verifies    │
│                                                  Returns result      │
└──────────────────────────────────────────────────────────────────────┘

Critical constraint: startSimBinding() cannot succeed unless enrollment has produced both a deepId and a sessionId. The plugin enforces this — calling startSimBinding() before the onEnrollment callback fires throws DeepIdErrorCode.enrollmentNotComplete.


Requirements #

Platform Minimum version
Android API 21 (Android 5.0)
iOS 13.0
Flutter 3.10.0
Dart SDK ≥ 3.0.0

Installation #

Step 1 — Place the SDK binaries as described in Prerequisites.

Step 2 — Add the plugin to your pubspec.yaml:

dependencies:
  deepidsdk_flutter: ^1.0.0

Step 3 — If your SIM binding flow requires runtime permissions on Android (it does — see below), also add permission_handler:

dependencies:
  permission_handler: ^11.0.0

Step 4 — Fetch dependencies:

flutter pub get

Android setup #

Permissions #

The plugin's AndroidManifest.xml auto-merges the following static permissions into your app at build time — you do not need to add them yourself:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />

READ_PHONE_STATE and SEND_SMS are dangerous permissions (protection level: dangerous). They are declared in the manifest but the user must grant them at runtime before startSimBinding() is called. Request them immediately before launching the SIM binding flow:

import 'package:permission_handler/permission_handler.dart';

Future<bool> _requestSimBindingPermissions() async {
  final statuses = await [Permission.phone, Permission.sms].request();
  return statuses[Permission.phone]!.isGranted &&
         statuses[Permission.sms]!.isGranted;
}

Without both grants the SDK cannot read the SIM list or send the silent verification SMS — startSimBinding() will fail or return an error.

build.gradle — minSdk #

Ensure your app module's android/app/build.gradle sets minSdk to at least 21:

android {
    defaultConfig {
        minSdk 21
    }
}

Activity (auto-registered) #

The plugin registers SimBindingActivity (a transparent Compose host) in its own manifest. It is never launched directly; the plugin starts it internally when startSimBinding() is called. No changes to your manifest are needed.


iOS setup #

CocoaPods #

The plugin uses CocoaPods to link DeepIdSDK.xcframework and ShieldPtr.xcframework. You must have placed both xcframeworks under ios/Frameworks/ (see Prerequisites) before running pod install.

cd ios && pod install

The podspec pulls in the required system frameworks automatically: MessageUI and CoreTelephony.

Minimum iOS target: Your app target must be set to iOS 13.0 or later.
In Xcode: Target → General → Minimum Deployments → iOS 13.0, or in ios/Podfile:

platform :ios, '13.0'

Info.plist — usage descriptions #

iOS requires a usage description for any entitlement the SDK exercises. Add the following to your app's ios/Runner/Info.plist:

<!-- Required: the SDK probes motion sensor availability during enrollment -->
<key>NSMotionUsageDescription</key>
<string>DeepID Protect collects sensor availability information to assess
device integrity during enrollment.</string>

No other permission prompts appear at runtime — CoreTelephony, IDFV, and network access do not require user permission on iOS.


Integration walkthrough #

1. Initialize the SDK (once, at app startup) #

Call DeepId.initialize() as early as possible — ideally in your root widget's initState. The call returns immediately after constructing the SDK; enrollment runs in the background.

import 'package:deepidsdk_flutter/deepidsdk_flutter.dart';

@override
void initState() {
  super.initState();
  _initDeepId();
}

Future<void> _initDeepId() async {
  try {
    await DeepId.initialize(
      appKey: 'YOUR_APP_KEY',
      appSecret: 'YOUR_APP_SECRET',
      onEnrollment: (EnrollmentResult result) {
        // Fires once when deepId + sessionId are ready.
        // Only now is it safe to show the SIM binding CTA.
        setState(() {
          _deepId = result.deepId;
          _sessionId = result.sessionId;
          _enrollmentReady = true;
        });
      },
      onEnrollmentError: (DeepIdException error) {
        // Enrollment failed — show an error state.
        setState(() => _enrollmentError = error.message);
      },
      enrollmentTimeout: const Duration(seconds: 30),
    );
  } on DeepIdException catch (e) {
    // initialize() itself failed — bad credentials or SDK construction error.
    setState(() => _enrollmentError = e.message);
  }
}

initialize() throws DeepIdException only if the credentials are missing or the SDK cannot be constructed. Network/server errors during the background enrollment are delivered via onEnrollmentError, not as exceptions from initialize().

2. Gate the SIM binding CTA on enrollment #

Never show the "Bind SIM" button until onEnrollment has fired. Show a loading indicator or a disabled state in the interim:

// In your widget build:
FilledButton(
  onPressed: _enrollmentReady ? _startSimBinding : null,
  child: Text(_enrollmentReady ? 'Bind SIM' : 'Enrolling…'),
)

3. Request Android permissions (Android only) #

Request READ_PHONE_STATE + SEND_SMS immediately before launching the flow, not at app startup — the system associates the rationale dialog with the action that requires the permission:

Future<void> _startSimBinding() async {
  if (Platform.isAndroid) {
    final statuses = await [Permission.phone, Permission.sms].request();
    final granted = statuses[Permission.phone]!.isGranted &&
                    statuses[Permission.sms]!.isGranted;
    if (!granted) {
      // Show permission rationale to the user and return.
      return;
    }
  }

  try {
    final result = await DeepId.startSimBinding(
      phoneNumber: '+911234567890',  // optional
    );
    print('Bound: ${result.deepId} / ${result.mobile}');
  } on DeepIdException catch (e) {
    if (e.code == DeepIdErrorCode.userCancelled) {
      // User dismissed the sheet — treat as a no-op, not an error.
    } else {
      // Real failure — show error message.
      print('SIM binding failed: ${e.message}');
    }
  }
}

4. Phone number parameter #

startSimBinding({String? phoneNumber}) accepts an optional E.164-formatted number:

  • Provided: Pre-fills the number field in the native sheet. The user still confirms before verification is sent.
  • Omitted: The native UI displays all available SIMs; the user selects one and enters their number.

API reference #

DeepId class #

All methods are static. The class cannot be instantiated.


DeepId.initialize()

static Future<void> initialize({
  required String appKey,
  required String appSecret,
  void Function(EnrollmentResult)? onEnrollment,
  void Function(DeepIdException)? onEnrollmentError,
  Duration enrollmentTimeout = const Duration(seconds: 150),
})

Constructs and starts the native SDK. Returns as soon as the SDK object is created — does not block until enrollment finishes.

Parameter Description
appKey Your DeepID app key. Must not be empty.
appSecret Your DeepID app secret. Must not be empty.
onEnrollment Fires once when the server returns both deepId and sessionId.
onEnrollmentError Fires if enrollment times out or fails. Optional.
enrollmentTimeout How long to wait for enrollment. Defaults to 150 s.

Throws DeepIdException if credentials are invalid or SDK construction fails.

If you call initialize() a second time (e.g., hot restart), the existing SDK instance is reused — no duplicate initialization occurs.


DeepId.onEnrollment()

static void onEnrollment({
  required void Function(EnrollmentResult result) onSuccess,
  void Function(DeepIdException error)? onError,
  Duration timeout = const Duration(seconds: 30),
})

Registers an enrollment callback independently of initialize(). Useful when you need to attach a callback in a different part of the widget tree than where you called initialize().

  • If enrollment has already completed, onSuccess fires on the next event-loop tick.
  • If enrollment has not completed, onSuccess fires the moment the native SDK delivers the result.
  • If timeout expires before enrollment completes, onError receives DeepIdErrorCode.enrollmentTimeout.

DeepId.startSimBinding()

static Future<SimBindingResult> startSimBinding({
  String? phoneNumber,
})

Presents the native SIM binding UI. Awaits user interaction and verification.

Returns a SimBindingResult on success. Throws DeepIdException on any failure including user cancellation.

Preconditions:

  • initialize() must have been called.
  • onEnrollment must have fired — i.e., both deepId and sessionId must be available.
  • On Android: READ_PHONE_STATE and SEND_SMS must be granted.

DeepId.isInitialized

static Future<bool> get isInitialized

Returns true if initialize() has been successfully called. Does not indicate that enrollment is complete — use the onEnrollment callback for that.


DeepId.deepId / DeepId.sessionId

static String? get deepId
static String? get sessionId

Synchronous accessors for the last enrollment result. Both return null until onEnrollment has fired at least once in this process. Safe to call from any isolate.


EnrollmentResult #

Delivered to the onEnrollment / onSuccess callback.

Field Type Description
deepId String Unique device fingerprint token. Non-empty.
sessionId String Current enrollment session identifier. Non-empty.

SimBindingResult #

Returned by a successful startSimBinding() call.

Field Type Description
success bool Always true when the object is returned (errors throw instead).
deepId String Device token at the time of binding.
sessionId String Session identifier at the time of binding.
mobile String Verified phone number. See Platform differences.
message String Human-readable confirmation string from the SDK.

DeepIdException #

Thrown by initialize() and startSimBinding(), and passed to error callbacks.

Property Type Description
code DeepIdErrorCode Typed error code.
message String Human-readable description.

DeepIdErrorCode #

Code When it occurs
notInitialized startSimBinding() called before initialize().
invalidAppKey appKey was blank when calling initialize().
invalidAppSecret appSecret was blank when calling initialize().
noContext Android: no Activity context available during initialize(). Call after the app is fully running.
noViewController iOS: no root UIViewController available to present the sheet.
initFailed SDK construction failed (network or server error during setup).
enrollmentFailed Backend rejected the enrollment request.
enrollmentTimeout Enrollment did not complete within the specified timeout.
enrollmentNotComplete startSimBinding() called before onEnrollment has fired.
simBindingFailed Verification flow failed on the carrier or server side.
alreadyInProgress startSimBinding() called while another SIM binding flow is still running.
userCancelled User dismissed the native SIM binding sheet without completing.
unknown Catch-all for unexpected native errors.

Error handling #

Distinguish cancellation from failure #

User cancellation is a normal UX path and should not be treated as an error:

try {
  final result = await DeepId.startSimBinding();
  // success path
} on DeepIdException catch (e) {
  switch (e.code) {
    case DeepIdErrorCode.userCancelled:
      // User tapped back / dismissed — no error to show
      break;
    case DeepIdErrorCode.enrollmentNotComplete:
      // Logic bug — show internal error or restart enrollment
      break;
    default:
      _showErrorBanner(e.message);
  }
}

Enrollment timeout #

The default timeout is 150 seconds. Increase it on slow networks if needed, or lower it to surface failures faster:

await DeepId.initialize(
  appKey: key,
  appSecret: secret,
  enrollmentTimeout: const Duration(seconds: 60),
  onEnrollmentError: (e) {
    if (e.code == DeepIdErrorCode.enrollmentTimeout) {
      // Offer a "Retry" button that calls initialize() again
    }
  },
);

Retry after failure #

initialize() is idempotent — calling it again reuses the existing native SDK instance if it was already constructed successfully. If the initial SDK construction failed, calling initialize() again creates a fresh instance.


Platform differences #

mobile field in SimBindingResult #

Platform Value
Android Phone number returned by the carrier after successful verification — guaranteed to be the real verified number.
iOS The verified phone number from the server response if the backend returns one; otherwise the phoneNumber argument passed to startSimBinding(). Pass phoneNumber explicitly to ensure the field is populated even if the server omits it.

Design your backend to treat the mobile value as informational on iOS, and rely on deepId + sessionId as the authoritative binding proof.

Permissions #

Permission Android iOS
Phone / SIM access READ_PHONE_STATE, READ_PHONE_NUMBERS (runtime, dangerous) No prompt — IDFV and CoreTelephony are available without permission
SMS sending SEND_SMS (runtime, dangerous) No prompt — MessageUI framework used
Motion / sensors None NSMotionUsageDescription in Info.plist (no prompt at runtime; CMMotionActivityManager prompt only if used)
Network INTERNET, ACCESS_NETWORK_STATE, ACCESS_WIFI_STATE (install-time, normal) No prompt

SIM binding UI #

Android iOS
Presentation Full Activity launched via startActivityForResult UIHostingController presented as a .pageSheet
SIM selection Jetpack Compose UI inside a transparent Activity SwiftUI SimBindingEntryView
Dismissal detection onActivityResult with RESULT_CANCELED UIAdaptivePresentationControllerDelegate + viewDidDisappear guard

Troubleshooting #

Framework 'DeepIdSDK' not found or Framework 'ShieldPtr' not found (iOS linker) #

Most commonly caused by one or both xcframeworks not being placed at the expected paths. Verify both files exist:

~/.pub-cache/hosted/pub.flutter-io.cn/deepidsdk_flutter-<version>/ios/Frameworks/DeepIdSDK.xcframework
~/.pub-cache/hosted/pub.flutter-io.cn/deepidsdk_flutter-<version>/ios/Frameworks/ShieldPtr.xcframework

(See Prerequisites.) Then clean the pod cache and reinstall:

cd ios
rm -rf Pods .symlinks Podfile.lock
pod install

The xcframework container name must also match the inner .framework name — the pod expects DeepIdSDK.xcframework containing DeepIdSDK.framework, and ShieldPtr.xcframework containing ShieldPtr.framework. If you received a differently named xcframework, rename the outer directory to match before running pod install.

Gradle error: "DeepID Protect SDK AAR is missing or empty" (Android) #

The build script requires the AAR at ~/.pub-cache/hosted/pub.flutter-io.cn/deepidsdk_flutter-<version>/android/libs/deepidsdk.aar. This file is not included in the pub.flutter-io.cn package — you must obtain it from DeepID (see Prerequisites) and place it at that exact path before running flutter build or flutter run.

.swiftinterface error: 'X' is not a member type of class 'DeepIdSDK.X' #

This is a Swift module/class name collision that occurs when a public class shares its name with the containing module. The DeepIdSDK xcframework avoids this by naming its public client class SimBindingClient (module DeepIdSDK). If you see this error, verify that you are using an xcframework built with BUILD_LIBRARY_FOR_DISTRIBUTION=YES and that the binary has not been renamed.

Enrollment times out on first run (Android) #

On first install the SDK enrolls the device with the DeepID backend. On slow networks this can exceed 30 seconds. Increase enrollmentTimeout and prompt the user to check their network connection if enrollment fails.

NO_CONTEXT error on Android #

initialize() must be called after the Flutter engine is attached to an Activity. Calling it in a background isolate or before the first frame is rendered will fail with this error. Place the call in initState() of your root widget or inside WidgetsBinding.instance.addPostFrameCallback.

ALREADY_IN_PROGRESS error #

Only one SIM binding flow can run at a time. If the user navigates away and back, the original Future from the first startSimBinding() call is still pending. Disable the "Bind SIM" button while startSimBinding() is in flight:

bool _simBindingInFlight = false;

// In your button:
onPressed: (_enrollmentReady && !_simBindingInFlight) ? _startSimBinding : null,

// In _startSimBinding:
setState(() => _simBindingInFlight = true);
try {
  final result = await DeepId.startSimBinding();
  // ...
} finally {
  setState(() => _simBindingInFlight = false);
}
0
likes
0
points
147
downloads

Publisher

verified publishersurepass.io

Weekly Downloads

Flutter plugin for the DeepID SDK (Android + iOS). Provides SIM binding, device enrollment, and fraud detection capabilities.

Homepage

License

unknown (license)

Dependencies

flutter

More

Packages that depend on deepidsdk_flutter

Packages that implement deepidsdk_flutter