> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# حزمة SDK للتحقق من الهوية في Flutter وDart

> حزمة Didit SDK لـ Flutter وDart مع iOS وAndroid الأصليين وكشف الحيوية وNFC. من $0.33 لكل فحص KYC كامل، و$10 مجانًا شهريًا، بدون عقود.



موجّه تكامل Flutter SDK
        نسخ الموجّه إلى الحافظة



      
      

\<pre>

\`Integrate Didit identity verification into my Flutter app using the native SDK.

\## Requirements
\- Flutter 3.3+, Dart 3.11+
\- iOS 13.0+ (NFC requires iOS 15+)
\- Android API 23+ (Android 6.0)

\## Installation
flutter pub add didit_sdk

iOS — the plugin picks the native variant from the $DiditSdkIosVariant Ruby global in ios/Podfile (or the DIDIT_SDK_IOS_VARIANT env var); valid values are 'all' (default), 'core', 'autodetection', 'nfc'. At the top of ios/Podfile:
$DiditSdkIosVariant = 'all'
platform :ios, ['all', 'nfc'].include?($DiditSdkIosVariant) ? '15.0' : '13.0'
Inside the target block, add the matching subspec:
pod 'DiditSDK/All', :podspec => 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.13.2/DiditSDK.podspec'
The tag in that URL must equal the native iOS SDK version the didit_sdk release pins (4.13.2 pins 4.13.2); do not use 'main', because the plugin requires that exact version and pod install fails when the URL resolves to a different one.
'all' and 'nfc' require platform :ios, '15.0'; 'core' and 'autodetection' run on iOS 13.0+.
Then: cd ios && pod install
Info.plist must include NSCameraUsageDescription, NSMicrophoneUsageDescription, NSPhotoLibraryUsageDescription (required) and NFCReaderUsageDescription if using NFC.

Android — for no-NFC builds add to android/gradle.properties:
diditSdkAndroidNfcEnabled=false
For NFC-enabled (default), add to android/app/build.gradle.kts inside android block:
packaging {
  resources {
      pickFirsts += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
  }
}

\## Backend: Create Session (your server)
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: { "workflow_id": DIDIT_WORKFLOW_ID, "vendor_data": "user-id" }
Response: { "session_id": "uuid", "session_token": "short-token", "url": "https://verify.didit.me/session/short-token", "status": "Not Started" }
Send session_token to the Flutter app (never expose DIDIT_API_KEY to the device).

\## Integration
import 'package:didit_sdk/sdk_flutter.dart';

// Option A: With session token from your backend (recommended)
final result = await DiditSdk.startVerification('session-token-from-backend');

// Option B: With workflow ID (SDK creates session internally)
final result = await DiditSdk.startVerificationWithWorkflow('your-workflow-id',
vendorData: 'user-123',
);

// Handle result
switch (result) {
case VerificationCompleted(:final session):
if (session.status == VerificationStatus.approved) { /* grant access */ }
case VerificationCancelled():
// User dismissed the verification flow
case VerificationFailed(:final error):
// SDK error — show error.message
}

\## Result Statuses (VerificationStatus enum)
\- VerificationStatus.approved — user identity verified, grant access
\- VerificationStatus.declined — verification failed, show retry or contact support
\- VerificationStatus.pending  — needs manual review by your compliance team

\## Error Types (VerificationErrorType enum)
sessionExpired, networkError, cameraAccessDenied, notInitialized, apiError, retryBlocked, unknown

\## Advanced Session Parameters
contact_details, expected_details, and metadata are only available via the Session Token method (your backend creates the session). The Dart startVerificationWithWorkflow API only accepts workflowId, vendorData, and config.

\## Full Verification Results
The SDK only returns the status. Full decision data (document fields, face match scores, AML hits, etc.) arrives via webhook to your backend.
Set up webhooks: https://docs.didit.me/integration/webhooks

\## Rate Limits (session creation)
\- 600 session-create requests / minute per x-api-key (see https://docs.didit.me/integration/rate-limiting)
\- On 429, retry honouring Retry-After / X-RateLimit-* headers

\## Environment Variables (backend only — never expose in the app)
\- DIDIT_API_KEY — from Didit Console > API & Webhooks
\- DIDIT_WORKFLOW_ID — from Didit Console > Workflows

\## Docs
\- Flutter SDK: https://docs.didit.me/integration/native-sdks/flutter-sdk
\- GitHub: https://github.com/didit-protocol/sdk-flutter
\- pub.dev: https://pub.dev/packages/didit_sdk
\- Webhooks: https://docs.didit.me/integration/webhooks
\- API Reference: https://docs.didit.me/api-reference/overview`

\</pre>



      
      

الصق في

مكوّن إضافي متعدد المنصات لـ Flutter يغلّف حزمتي SDK الأصليتين لنظامي iOS وAndroid، ويوفّر واجهة Dart موحّدة للتحقق من الهوية.> [card] [مستودع GitHub](https://github.com/didit-protocol/sdk-flutter)
>
> اطّلع على الشيفرة المصدرية والأمثلة على GitHub> [card] [حزمة pub.dev](https://pub.dev/packages/didit_sdk)
>
> didit\_sdk> [!NOTE]
>
> ### **SDK الأصلي**: هذا هو النهج الموصى به لتطبيقات Flutter. يغلّف الـ SDK حزمتي SDK الأصليتين لنظامي iOS وAndroid عبر قنوات المنصة، لتوفير أفضل تجربة للمستخدم، ومعالجة محسّنة للكاميرا، ودعم كامل لـ NFC.***## المتطلبات| المتطلب | أدنى إصدار |
| - | - |
| Flutter | 3.3+ |
| Dart | 3.11+ |### متطلبات المنصة| المنصة | أدنى إصدار | ملاحظات |
| - | - | - |
| iOS | 13.0+ | قراءة جواز السفر عبر NFC تتطلب iOS 15.0+ |
| Android | API 23+ (6.0) | Java 17+ |> [!NOTE]
>
> **الموافقة البيومترية تتطلب SDK الأصلي 4.10.0 أو أحدث.** بالنسبة للأشخاص الخاضعين لقوانين Illinois أو Texas أو Colorado، يمكن لـ Didit أن تطلب [الموافقة البيومترية](https://docs.didit.me/ar/getting-started/biometric-consent.md) الصريحة قبل أول التقاط للوجه. يعرض إصدار Flutter SDK الذي يثبّت SDK الأصلي لـ iOS وAndroid بالإصدار 4.10.0 أو أحدث هذه الخطوة. الإصدارات الأقدم لا تستطيع عرض هذه الخطوة: تعمل جلساتها كما كانت من قبل، ويسجّل القرار الموافقة على أنها لم تُجمع، مع `biometric_consent.reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK"`. قم بالترقية إذا كنت تتحقق من أشخاص في تلك الولايات.***## التثبيت```bash theme={null}
flutter pub add didit_sdk
```أو أضفه يدويًا إلى ملف `pubspec.yaml`:```yaml theme={null}
dependencies:
  didit_sdk: ^4.13.2
```ثم شغّل:```bash theme={null}
flutter pub get
```### متغيرات حزمة SDK الأصليةتتضمن حزمة `didit_sdk` حزمة SDK الأصلية كاملة.
لتقليل حجم الملف الثنائي، استخدم بدلًا من ذلك إحدى حزم المتغيرات، فهي تعرض واجهة Dart نفسها بالضبط، وتثبّت إصدار SDK الأصلي المطابق على المنصتين:| الحزمة | الالتقاط التلقائي | قراءة جواز السفر عبر NFC | الحد الأدنى لإصدار iOS |
| - | - | - | - |
| `didit_sdk` (الافتراضية) | نعم | نعم | 15.0 |
| `didit_sdk_core` | لا | لا | 13.0 |
| `didit_sdk_autodetection` | نعم | لا | 13.0 |
| `didit_sdk_nfc` | لا | نعم | 15.0 |```yaml theme={null}
dependencies:
  didit_sdk_core: ^4.13.2
```أضف حزمة واحدة فقط من هذه الحزم، واستورد من الحزمة التي اخترتها، على سبيل المثال `package:didit_sdk_core/sdk_flutter.dart`.### إعداد iOSتدعم الإضافة مديري تبعيات iOS في Flutter كليهما: Swift Package Manager وCocoaPods.#### Swift Package Managerإذا كان دعم Swift Package Manager مفعّلًا في إصدار Flutter لديك (فعّله باستخدام `flutter config --enable-swift-package-manager` في Flutter 3.24+)، تُستخدم الإضافة كحزمة Swift تلقائيًا.
لا حاجة إلى أي إعداد في Podfile، إذ تُحل تبعية DiditSDK الأصلية من GitHub تلقائيًا.
اضبط الحد الأدنى لإصدار النشر في iOS لتطبيقك في Xcode (الهدف Runner والمشروع) على الحد الأدنى للحزمة التي اخترتها: 15.0 لـ `didit_sdk` و`didit_sdk_nfc`، و13.0 لـ `didit_sdk_core` و`didit_sdk_autodetection`.#### CocoaPodsمع `didit_sdk`، تختار الإضافة متغير SDK الأصلي لـ iOS من `$DiditSdkIosVariant`، وهو متغير عام في Ruby تضبطه في ملف `ios/Podfile` الخاص بتطبيقك (لا توجد حزمة DiditSDK في CocoaPods trunk):```ruby theme={null}
# Pick one: 'all', 'core', 'autodetection', 'nfc'
$DiditSdkIosVariant = 'all'

didit_sdk_ios_deployment_target = ['all', 'nfc'].include?($DiditSdkIosVariant) ? '15.0' : '13.0'
platform :ios, didit_sdk_ios_deployment_target

didit_sdk_ios_pod = case $DiditSdkIosVariant
                    when 'all' then 'DiditSDK/All'
                    when 'core' then 'DiditSDK/Core'
                    when 'autodetection' then 'DiditSDK/AutoDetection'
                    when 'nfc' then 'DiditSDK/NFC'
                    end
didit_sdk_ios_podspec = 'https://raw.githubusercontent.com/didit-protocol/sdk-ios/4.13.2/DiditSDK.podspec'

target 'Runner' do
  use_frameworks!

  pod didit_sdk_ios_pod, :podspec => didit_sdk_ios_podspec

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = didit_sdk_ios_deployment_target
    end
  end
end
```إذا استخدمت حزمة متغير (`didit_sdk_core` أو `didit_sdk_autodetection` أو `didit_sdk_nfc`)، فهي تثبّت الوحدة الفرعية الأصلية الخاصة بها مباشرة وتتجاهل `$DiditSdkIosVariant`. اجعل سطر `pod` في Podfile وإصدار النشر متطابقين مع تلك الحزمة، على سبيل المثال `pod 'DiditSDK/Core', :podspec => ...` مع `platform :ios, '13.0'`.> [!NOTE]
>
> المتغيران `all` و`nfc` يتطلبان هدف نشر iOS 15.0+، بينما يمكن للمتغيرين `core` و`autodetection` استهداف iOS 13.0. عند التبديل بين المتغيرات، نظّف CocoaPods أولًا (`rm -rf Pods Podfile.lock`) حتى لا يُعاد استخدام متغير SDK السابق.> [!WARNING]
>
> يجب أن يطابق الوسم في رابط podspec إصدار SDK الأصلي لـ iOS الذي يثبّته إصدار `didit_sdk` لديك
> (الإصدار `4.13.2` يثبّت SDK الأصلي `4.13.2`). إذا كان الرابط يشير إلى وسم أقدم، يفشل `pod install` في حل التبعية، لأن CocoaPods يجد podspec يعلن ذلك الإصدار الأقدم، بينما تتطلب الإضافة تطابقًا تامًا.
> عند ترقية `didit_sdk`، حدّث الوسم في هذا الرابط إلى الإصدار نفسه.### إعداد Androidتعتمد الإضافة افتراضيًا على حزمة SDK الكاملة لـ Android مع NFC. لبناء التطبيق بدون NFC، أضف هذا إلى `android/gradle.properties`:```properties theme={null}
diditSdkAndroidNfcEnabled=false
```يغيّر هذه التبعية من `me.didit:didit-sdk` إلى `me.didit:didit-sdk-core`، فتُزال وحدة قارئ NFC وتبعياتها JMRTD وSCUBA وBouncyCastle.عند تفعيل NFC (الافتراضي)، أضف قاعدة التجميع هذه إلى `android/app/build.gradle.kts` لحل ملف بيانات وصفية مكرر من BouncyCastle:```kotlin theme={null}
android {
    packaging {
        resources {
            pickFirsts += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
        }
    }
}
```بدون هذه القاعدة يفشل البناء بخطأ `mergeDebugJavaResource`. لا تلزم القاعدة عندما يكون `diditSdkAndroidNfcEnabled=false`.> [!NOTE]
>
> تُحل حزمة SDK الأصلية لـ Android من مستودع Maven بعيد على GitHub (منذ الإضافة 3.4.0)، ولا حاجة إلى أي إعداد Maven يدوي في تطبيقك.***## الأذونات### iOSأضف المفاتيح التالية إلى ملف `Info.plist` الخاص بالتطبيق:| الإذن | مفتاح Info.plist | الوصف | مطلوب |
| - | - | - | - |
| الكاميرا | `NSCameraUsageDescription` | مسح المستندات والتحقق من الوجه | نعم |
| الميكروفون | `NSMicrophoneUsageDescription` | تسجيل الفيديو لفحوص كشف الحيوية | نعم |
| مكتبة الصور | `NSPhotoLibraryUsageDescription` | رفع المستندات من معرض الجهاز | نعم |
| NFC | `NFCReaderUsageDescription` | قراءة شرائح NFC في جوازات السفر وبطاقات الهوية | عند استخدام NFC |
| الموقع | `NSLocationWhenInUseUsageDescription` | تحديد الموقع الجغرافي لمنع الاحتيال | اختياري |> [!WARNING]
>
> إذا كان أي مفتاح خصوصية مطلوب في iOS مفقودًا، فإن iOS ينهي التطبيق فور محاولة الحزمة الوصول إلى ذلك المورد المحمي. على سبيل المثال، يؤدي غياب `NSCameraUsageDescription` إلى تعطل التطبيق عندما يضغط المستخدم زر التقاط الصورة في كاميرا المستندات.#### إعدادات NFCلتفعيل قراءة NFC لجوازات السفر وبطاقات الهوية التي تحتوي على شرائح:1) **أضف إمكانية NFC** في Xcode:
   * حدّد الهدف (target) الخاص بك > **Signing & Capabilities** > **+ Capability** > **Near Field Communication Tag Reading**

2) **أضف معرّفات ISO7816 إلى `Info.plist`:**```xml theme={null}
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
    <string>D23300000045737445494420763335</string>
    <string>A0000002471001</string>
    <string>A0000002472001</string>
    <string>00000000000000</string>
    <string>D392F000260100000001</string>
</array>
```يتيح `D392F000260100000001` للحزمة قراءة Japan My Number Card عبر [JPKI](https://docs.didit.me/ar/core-technology/nfc-verification/overview.md#بطاقة-ماي-نمبر-اليابانية-jpki)، فبدونه لا يُبلّغ iOS الحزمة بوجود البطاقة. قراءة My Number Card غير مفعّلة في بيئة الإنتاج بعد، ولا يغيّر هذا المعرّف شيئًا بالنسبة إلى المستندات الأخرى، لذا يمكنك إضافته الآن.3) **أضف ملف الاستحقاقات (entitlements)** مع تفعيل قراءة وسوم NFC:```xml theme={null}
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>TAG</string>
</array>
```تأكد من أن ملف توصيف التوفير (provisioning profile) الخاص بالتطبيق يتضمن إمكانية NFC Tag Reading. إذا لم يكن معرّف الحزمة (bundle ID) مهيأً لـ NFC في حساب Apple Developer الخاص بك، فسيفشل Xcode في التوقيع بسبب غياب الاستحقاق `com.apple.developer.nfc.readersession.formats`.إعدادات NFC هذه غير لازمة للمتغيرين `core` أو `autodetection` (`$DiditSdkIosVariant`)، ولا للحزمتين `didit_sdk_core` / `didit_sdk_autodetection`، إذ لا يقرأ أيٌّ منها الشرائح.### Androidتُعلن الحزمة الأذونات التالية في ملف `AndroidManifest.xml` الخاص بها، وتُدمج تلقائيًا:| الإذن | الوصف | مطلوب |
| - | - | - |
| `INTERNET` | الوصول إلى الشبكة للتواصل مع واجهة برمجة التطبيقات | نعم |
| `ACCESS_NETWORK_STATE` | رصد توفر الشبكة | نعم |
| `CAMERA` | مسح المستندات والتحقق من الوجه | نعم |
| `NFC` | قراءة شرائح NFC في جوازات السفر وبطاقات الهوية | عند استخدام NFC |تُعلن ميزتا الكاميرا وNFC في الجهاز كميزتين اختياريتين (`android:required="false"`)، فيمكن تثبيت التطبيق على أجهزة لا تملك هذه الميزات. عندما تكون `diditSdkAndroidNfcEnabled=false`، لا تضيف الحزمة إذن NFC ولا الميزة الخاصة به على Android.#### أذونات وقت التشغيلتتولى الحزمة طلبات أذونات وقت التشغيل في Android تلقائيًا. عندما يصل المستخدم إلى خطوة تتطلب الوصول إلى الكاميرا:1) تطلب الحزمة إذن الكاميرا إذا لم يكن ممنوحًا من قبل
2) إذا **رفض** المستخدم الإذن، تظهر رسالة خطأ مع زر **Try Again**
3) إذا **منح** المستخدم الإذن، يتابع مسار التحققلست بحاجة إلى طلب إذن الكاميرا في كود التطبيق قبل استدعاء `startVerification()`، فالحزمة تدير ذلك داخليًا.***## البدء السريع```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

// Start verification with a session token from your backend
final result = await DiditSdk.startVerification('your-session-token');

switch (result) {
  case VerificationCompleted(:final session):
    if (session.status == VerificationStatus.approved) {
      print('Identity verified!');
    }
  case VerificationCancelled():
    print('User cancelled');
  case VerificationFailed(:final error):
    print('Error: ${error.message}');
}
```***## طرق التكاملتدعم الحزمة طريقتين للتكامل:### الطريقة 1: token الجلسة (موصى به للإنتاج)أنشئ جلسة على الخادم الخلفي باستخدام [واجهة برمجة تطبيقات إنشاء جلسة التحقق](https://docs.didit.me/ar/sessions-api/create-session.md)، ثم مرّر token إلى الحزمة:```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

// Your backend creates a session and returns the token
final sessionToken = await yourBackend.createVerificationSession(userId);

// Pass the token to the SDK
final result = await DiditSdk.startVerification(sessionToken);
```يمنحك هذا النهج تحكمًا كاملًا في:- ربط الجلسات بمستخدميك (`vendor_data`)
- ضبط البيانات الوصفية المخصصة
- ضبط عمليات الاستدعاء لكل جلسة### الطريقة 2: معرّف سير العمل (تكامل أبسط)للتكاملات الأبسط، يمكن للحزمة إنشاء الجلسات مباشرة باستخدام معرّف سير العمل الخاص بك، دون الحاجة إلى خادم خلفي:```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

final result = await DiditSdk.startVerificationWithWorkflow(
  'your-workflow-id',
  vendorData: 'user-123',
  config: const DiditConfig(loggingEnabled: true),
);
```> [!NOTE]
>
> معاملات الجلسة المتقدمة (`contact_details` و`expected_details` و`metadata`) مدعومة **فقط** عبر طريقة token الجلسة، حيث يستدعي خادمك [واجهة برمجة تطبيقات إنشاء الجلسة](https://docs.didit.me/ar/sessions-api/create-session.md) بدعم كامل لهذه المعاملات. مرّر `session_token` المُعاد إلى `DiditSdk.startVerification()`.***## الإعداداتخصّص سلوك SDK بتمرير كائن `DiditConfig`:```dart theme={null}
final result = await DiditSdk.startVerification(
  'your-session-token',
  config: const DiditConfig(
    languageCode: 'es',       // Force Spanish language
    showLanguageSelector: true, // Let the user change language
    fontFamily: 'Avenir',     // Custom font
    loggingEnabled: true,     // Enable debug logging
  ),
);
```مع `startVerificationWithWorkflow`، مرّر `config` كمعامل مسمّى:```dart theme={null}
final result = await DiditSdk.startVerificationWithWorkflow(
  'your-workflow-id',
  vendorData: 'user-123',
  config: const DiditConfig(
    languageCode: 'es',
    showLanguageSelector: true,
    loggingEnabled: true,
  ),
);
```### خيارات الإعداد| الخاصية | النوع | القيمة الافتراضية | الوصف |
| - | - | - | - |
| `languageCode` | `String?` | إعدادات المنطقة واللغة في الجهاز | رمز اللغة وفق ISO 639-1 (مثل `"en"` و`"fr"` و`"ar"`) |
| `showLanguageSelector` | `bool` | `false` | عرض محدد اللغة بنمط الويب على شاشة الترحيب |
| `fontFamily` | `String?` | خط النظام | اسم عائلة خط مخصص (يجب تسجيله أصليًا) |
| `loggingEnabled` | `bool` | `false` | تفعيل تسجيل تصحيح الأخطاء في SDK |
| `showCloseButton` | `bool` | `true` | عرض زر الإغلاق (X) على شاشات خطوات التحقق |
| `showExitConfirmation` | `bool` | `true` | عرض نافذة تأكيد عندما يحاول المستخدم الخروج |
| `closeOnComplete` | `bool` | `false` | إغلاق واجهة التحقق تلقائياً عند الاكتمال |
| `defaultDocumentCamera` | `CameraLens?` | الافتراضي الأصلي (`CameraLens.back`) | العدسة المستخدمة عند فتح شاشة التقاط المستند |
| `defaultLivenessCamera` | `CameraLens?` | الافتراضي الأصلي (`CameraLens.front`) | العدسة المستخدمة عند فتح شاشة التقاط الوجه (كشف الحيوية) |
| `showDocumentCameraSwitchButton` | `bool` | `true` | عرض مبدّل الكاميرا أثناء الالتقاط على شاشة التقاط المستند |
| `showLivenessCameraSwitchButton` | `bool` | `true` | عرض مبدّل الكاميرا أثناء الالتقاط على شاشة التقاط الوجه |### خيارات الكاميراتتطابق خيارات الكاميرا مع [خيارات كاميرا Web SDK](https://docs.didit.me/ar/integration/web-sdks/javascript-sdk.md#خيارات-الكاميرا).
كل عدسة هي `CameraLens.front` أو `CameraLens.back`.
إذا لم يكن الجهاز يحتوي على العدسة المطلوبة، يستخدم SDK أول كاميرا متوفرة لديه.
يُخفى مبدّل الكاميرا أيضاً على الأجهزة التي تحتوي على كاميرا واحدة فقط.
اضبط خيار المبدّل على `false` لتثبيت المستخدم على العدسة الافتراضية لتلك الخطوة.```dart theme={null}
config: const DiditConfig(
  defaultLivenessCamera: CameraLens.back,
  showLivenessCameraSwitchButton: false,
),
```> [!NOTE]
>
> **السمات والألوان**: تُضبط الألوان والخلفيات وإعدادات شاشة المقدمة من خلال [إعدادات العلامة البيضاء](https://business.didit.me) في Didit Console، وليس في إعدادات SDK. وهذا يضمن هوية بصرية متسقة عبر جميع المنصات.***## دعم اللغاتيدعم SDK **54 لغة**. إذا لم تحدد أي لغة، يستخدم SDK لغة الجهاز مع الإنجليزية كلغة احتياطية.```dart theme={null}
// Use device locale (default)
await DiditSdk.startVerification(token);

// Force specific language
await DiditSdk.startVerification(token,
  config: const DiditConfig(languageCode: 'fr'),
);

// Let the user change language on the welcome screen
await DiditSdk.startVerification(token,
  config: const DiditConfig(showLanguageSelector: true),
);
```**[عرض جميع اللغات المدعومة ←](https://docs.didit.me/ar/integration/supported-languages.md)*****## معاملات الجلسة المتقدمةلا تُدعم المعاملات مثل `contact_details` و`expected_details` و`metadata` إلا من خلال طريقة Session Token. يستدعي الخادم الخاص بك [`POST /v3/session/`](https://docs.didit.me/ar/sessions-api/create-session) مع دعم كامل للمعاملات، ثم يمرر `session_token` المُرجَع إلى SDK.```dart theme={null}
// Your backend handles the full session creation:
// POST /v3/session/ with contact_details, expected_details, metadata, etc.
final sessionToken = await yourBackend.createSession(userId);

// The SDK only needs the token
final result = await DiditSdk.startVerification(sessionToken);
```> [!NOTE]
>
> لا تقبل طريقة Dart `startVerificationWithWorkflow` سوى `workflowId` و`vendorData` و`config`. كانت الإصدارات السابقة للإصدار 3.4.0 تعرض المعاملات `contactDetails` و`expectedDetails` و`metadata`، لكن أُزيلت لأن endpoint الخاص بـ Unilink لا يلتزم بها.***## معالجة النتائجتُعيد كلتا الطريقتين `startVerification` و`startVerificationWithWorkflow` قيمة `Future<VerificationResult>`. النتيجة فئة مغلقة (sealed class)، فاستخدم مطابقة الأنماط لتحديد النتيجة.### حالات النتيجة| الحالة | الوصف |
| - | - |
| `VerificationCompleted` | اكتمل تدفق التحقق (تحقق من `session.status` للحصول على النتيجة) |
| `VerificationCancelled` | ألغى المستخدم تدفق التحقق |
| `VerificationFailed` | حدث خطأ أثناء التحقق |### خصائص SessionData| الخاصية | النوع | الوصف |
| - | - | - |
| `sessionId` | `String` | معرّف الجلسة الفريد |
| `status` | `VerificationStatus` | `approved` أو `pending` أو `declined` |### أنواع الأخطاءيعرض التعداد `VerificationErrorType` الحالات التالية:| الخطأ | الوصف |
| - | - |
| `sessionExpired` | الجلسة منتهية الصلاحية |
| `networkError` | مشكلة في الاتصال بالشبكة |
| `cameraAccessDenied` | لم يتم منح إذن الكاميرا |
| `notInitialized` | لم تتم تهيئة SDK (Android فقط) |
| `apiError` | فشل طلب API |
| `retryBlocked` | تم تجاوز الحد الأقصى لعدد المحاولات للخطوة الحالية |
| `unknown` | خطأ آخر، راجع `error.message` للتفاصيل |### انقطاع الاتصالإذا فقد الجهاز اتصاله أثناء التحقق، يعرض SDK شاشة "No internet connection" فوق التدفق بدلاً من ترك خطوة في حالة تحميل.
يستمر SDK في التحقق في الخلفية، ويغلق الشاشة تلقائياً بمجرد إمكانية الوصول إلى Didit مرة أخرى، ويتابع المستخدم من الخطوة التي كان عليها.
تعرض الشاشة **Try Again** و، ما لم يكن زر الإغلاق معطلاً، **Exit Verification**.
يؤدي الخروج إلى إنهاء التحقق بنتيجة `VerificationFailed` يكون نوع الخطأ فيها `networkError`، لكي يعرف تطبيقك أن المستخدم خرج بسبب الاتصال.### مثال كامل لمعالجة النتائج```dart theme={null}
import 'package:didit_sdk/sdk_flutter.dart';

Future<void> verify(String token) async {
  final result = await DiditSdk.startVerification(token);

  switch (result) {
    case VerificationCompleted(:final session):
      switch (session.status) {
        case VerificationStatus.approved:
          print('Approved! Session: ${session.sessionId}');
          // User is verified — grant access
        case VerificationStatus.pending:
          print('Under review. Session: ${session.sessionId}');
          // Show "verification in progress" UI
        case VerificationStatus.declined:
          print('Declined. Session: ${session.sessionId}');
          // Handle declined verification
      }

    case VerificationCancelled(:final session):
      print('User cancelled.');
      if (session != null) {
        print('Session: ${session.sessionId}');
      }
      // Maybe show retry option

    case VerificationFailed(:final error):
      print('Error [${error.type}]: ${error.message}');
      // Handle error — show retry or contact support
  }
}
```***## مثال شامل من البداية إلى النهاية (الخادم الخلفي → Flutter SDK → webhook)التكامل الجاهز للإنتاج: ينشئ الخادم الخاص بك الجلسة، ويستقبل تطبيق Flutter الخاص بك `session_token`، ويُشغّل التدفق عبر SDK. يُرسَل القرار النهائي إلى الخادم عبر webhook، ولا تعتمد أبداً على نتيجة جانب العميل وحدها.### 1. الخادم الخلفي: إنشاء الجلسة```ts theme={null}
// Node.js / Express example. Run on your server. NEVER ship DIDIT_API_KEY to the device.
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/verification/start", async (req, res) => {
  const response = await fetch("https://verification.didit.me/v3/session/", {
    method: "POST",
    headers: {
      "x-api-key": process.env.DIDIT_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: req.body.userId, // your stable user id
    }),
  });

  if (!response.ok) {
    return res.status(500).json({ error: `Didit ${response.status}` });
  }

  const { session_id, session_token } = await response.json();
  // Persist session_id <-> userId so you can match the webhook later.
  res.json({ session_id, session_token });
});
```### 2. Flutter: تبادل token وتشغيل SDK```dart theme={null}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:didit_sdk/sdk_flutter.dart';

class VerifyButton extends StatefulWidget {
  const VerifyButton({super.key, required this.userId});
  final String userId;

  @override
  State<VerifyButton> createState() => _VerifyButtonState();
}

class _VerifyButtonState extends State<VerifyButton> {
  bool _loading = false;

  Future<void> _verify() async {
    setState(() => _loading = true);
    try {
      final resp = await http.post(
        Uri.parse('https://your-backend.example.com/api/verification/start'),
        headers: const {'Content-Type': 'application/json'},
        body: jsonEncode({'userId': widget.userId}),
      );
      final sessionToken = (jsonDecode(resp.body) as Map)['session_token'] as String;

      final result = await DiditSdk.startVerification(sessionToken);

      switch (result) {
        case VerificationCompleted(:final session):
          if (session.status == VerificationStatus.approved) {
            // Optimistic UI; rely on the webhook delivered to your backend as the source of truth.
          }
        case VerificationCancelled():
          // User dismissed the flow
          break;
        case VerificationFailed(:final error):
          // Show retry — error.type / error.message
          break;
      }
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _loading ? null : _verify,
      child: Text(_loading ? 'Starting...' : 'Verify identity'),
    );
  }
}
```### 3. الخادم الخلفي: استقبال القرار النهائي عبر webhook```ts theme={null}
app.post("/api/webhooks/didit", express.raw({ type: "application/json" }), (req, res) => {
  // 1. Verify the X-Signature header (see Webhooks docs)
  // 2. Parse the body
  const event = JSON.parse(req.body.toString());
  if (event.webhook_type === "status.updated") {
    // event.session_id, event.status ("Approved" | "Declined" | "In Review" | ...)
    // Look up the user by session_id and update their verification state.
  }
  res.sendStatus(200);
});
```راجع [دليل webhook](https://docs.didit.me/ar/integration/webhooks.md) لمعرفة تفاصيل التحقق من توقيع HMAC.***## مثال كامل```dart theme={null}
import 'package:flutter/material.dart';
import 'package:didit_sdk/sdk_flutter.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Didit SDK Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1A1A1A)),
        useMaterial3: true,
      ),
      home: const VerificationScreen(),
    );
  }
}

class VerificationScreen extends StatefulWidget {
  const VerificationScreen({super.key});

  @override
  State<VerificationScreen> createState() => _VerificationScreenState();
}

class _VerificationScreenState extends State<VerificationScreen> {
  final _tokenController = TextEditingController();
  bool _loading = false;

  @override
  void dispose() {
    _tokenController.dispose();
    super.dispose();
  }

  Future<void> _startVerification() async {
    final token = _tokenController.text.trim();
    if (token.isEmpty) {
      _showAlert('Error', 'Please enter a session token.');
      return;
    }

    setState(() => _loading = true);

    try {
      final result = await DiditSdk.startVerification(
        token,
        config: const DiditConfig(loggingEnabled: true),
      );

      switch (result) {
        case VerificationCompleted(:final session):
          _showAlert(
            'Verification Complete',
            'Status: ${session.status.name}\nSession: ${session.sessionId}',
          );
        case VerificationCancelled():
          _showAlert('Cancelled', 'The user cancelled the verification.');
        case VerificationFailed(:final error):
          _showAlert('Failed', '${error.type.name}: ${error.message}');
      }
    } catch (e) {
      _showAlert('Error', 'Unexpected error: $e');
    } finally {
      setState(() => _loading = false);
    }
  }

  void _showAlert(String title, String message) {
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Text(title),
        content: Text(message),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              TextField(
                controller: _tokenController,
                decoration: InputDecoration(
                  hintText: 'Enter session token...',
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                ),
                autocorrect: false,
              ),
              const SizedBox(height: 16),
              ElevatedButton(
                onPressed: _loading ? null : _startVerification,
                style: ElevatedButton.styleFrom(
                  backgroundColor: const Color(0xFF1A1A1A),
                  foregroundColor: Colors.white,
                  padding: const EdgeInsets.symmetric(vertical: 16),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                ),
                child: _loading
                    ? const SizedBox(
                        height: 20,
                        width: 20,
                        child: CircularProgressIndicator(
                          strokeWidth: 2,
                          color: Colors.white,
                        ),
                      )
                    : const Text(
                        'Start Verification',
                        style: TextStyle(fontWeight: FontWeight.w600),
                      ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```***


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.