full_identity_verification 0.1.4 copy "full_identity_verification: ^0.1.4" to clipboard
full_identity_verification: ^0.1.4 copied to clipboard

Flutter plugin for Blusalt Full Identity Verification SDK that supports both Android and iOS platforms.

example/lib/main.dart

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:full_identity_verification/enums.dart';
import 'package:full_identity_verification/full_identity_verification.dart';
import 'package:full_identity_verification/model.dart';

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

enum FullIdentitySDKType {
  documentPresent,
  docAbsentCustomSelector,
  docAbsentIdNumber,
  none
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _fullIdentityPlugin = BlusaltFullIdentityVerification();

  // Controllers
  TextEditingController clientIdController = TextEditingController(text: 'KZDLKJJEG9OP');
  TextEditingController appNameController = TextEditingController(text: 'identity-checks');
  TextEditingController apiKeyController = TextEditingController(text: 'QQ9NPHLILYMLDMHGNQR8XOAO6Z04SAXY');
  TextEditingController webHookUrlController = TextEditingController();
  TextEditingController referenceController = TextEditingController();
  TextEditingController thresholdController = TextEditingController();
  TextEditingController timeoutDurationController = TextEditingController();
  TextEditingController documentNumberController = TextEditingController();

  // Settings
  bool isDev = false;

  // Document type selection
  List<DocumentType> selectedDocumentTypes = [
    DocumentType.bvn,
    DocumentType.nin,
    DocumentType.passport,
  ];
  DocumentType selectedSingleDocType = DocumentType.bvn;
  LivenessFacialComparisonType selectedLivenessType =
      LivenessFacialComparisonType.motional;

  // Result
  FullIdentitySDKType sdkType = FullIdentitySDKType.none;
  BlusaltFullIdentityResultResponse? resultResponse;

  @override
  void dispose() {
    super.dispose();
    clientIdController.dispose();
    appNameController.dispose();
    apiKeyController.dispose();
    webHookUrlController.dispose();
    referenceController.dispose();
    thresholdController.dispose();
    timeoutDurationController.dispose();
    documentNumberController.dispose();
  }

  Future<void> startFullIdentitySDK() async {
    if (!_validateInputs()) return;

    resultResponse = await _fullIdentityPlugin.startFullIdentitySDK(
      apiKey: apiKeyController.text,
      appName: appNameController.text,
      clientId: clientIdController.text,
      isDev: isDev,
      webhookUrl: webHookUrlController.text.isEmpty
          ? null
          : webHookUrlController.text,
      reference: referenceController.text.isEmpty
          ? null
          : referenceController.text,
      documentTypeList: selectedDocumentTypes,
      timeoutDurationInSec: _validateDurationNumber(timeoutDurationController.text),
    );

    if (resultResponse?.blusaltFullIdentityProcess ==
        BlusaltFullIdentityProcess.completed) {
      setState(() {
        sdkType = FullIdentitySDKType.documentPresent;
      });
    } else {
      debugPrint('Error Code: ${resultResponse?.code ?? ''}');
      debugPrint('Error Message: ${resultResponse?.message ?? ''}');
      _showErrorDialog(resultResponse?.message ?? 'Unknown error occurred');
    }
  }

  Future<void> startDocAbsentWithCustomSelector() async {
    if (!_validateInputs()) return;

    resultResponse =
        await _fullIdentityPlugin.startDocAbsentWithCustomSelector(
      apiKey: apiKeyController.text,
      appName: appNameController.text,
      clientId: clientIdController.text,
      isDev: isDev,
      documentTypeList: selectedDocumentTypes,
      webhookUrl: webHookUrlController.text.isEmpty
          ? null
          : webHookUrlController.text,
      reference: referenceController.text.isEmpty
          ? null
          : referenceController.text,
      livenessFacialComparisonType: selectedLivenessType,
      thresholdInPercent: _validateNumber(thresholdController.text),
      timeoutDurationInSec: _validateDurationNumber(timeoutDurationController.text),
    );

    if (resultResponse?.blusaltFullIdentityProcess ==
        BlusaltFullIdentityProcess.completed) {
      setState(() {
        sdkType = FullIdentitySDKType.docAbsentCustomSelector;
      });
    } else {
      debugPrint('Error Code: ${resultResponse?.code ?? ''}');
      debugPrint('Error Message: ${resultResponse?.message ?? ''}');
      _showErrorDialog(resultResponse?.message ?? 'Unknown error occurred');
    }
  }

  Future<void> startDocAbsentWithIdNumber() async {
    if (!_validateInputs()) return;

    if (documentNumberController.text.isEmpty) {
      _showErrorDialog('Please enter document number');
      return;
    }

    resultResponse = await _fullIdentityPlugin.startDocAbsentWithIdNumber(
      apiKey: apiKeyController.text,
      appName: appNameController.text,
      clientId: clientIdController.text,
      isDev: isDev,
      documentType: selectedSingleDocType,
      documentNumber: documentNumberController.text,
      webhookUrl: webHookUrlController.text.isEmpty
          ? null
          : webHookUrlController.text,
      reference: referenceController.text.isEmpty
          ? null
          : referenceController.text,
      livenessFacialComparisonType: selectedLivenessType,
      thresholdInPercent: _validateNumber(thresholdController.text),
      timeoutDurationInSec: _validateDurationNumber(timeoutDurationController.text),
    );

    if (resultResponse?.blusaltFullIdentityProcess ==
        BlusaltFullIdentityProcess.completed) {
      setState(() {
        sdkType = FullIdentitySDKType.docAbsentIdNumber;
      });
    } else {
      debugPrint('Error Code: ${resultResponse?.code ?? ''}');
      debugPrint('Error Message: ${resultResponse?.message ?? ''}');
      _showErrorDialog(resultResponse?.message ?? 'Unknown error occurred');
    }
  }

  bool _validateInputs() {
    if (clientIdController.text.isEmpty ||
        appNameController.text.isEmpty ||
        apiKeyController.text.isEmpty) {
      _showErrorDialog('Please fill in Client ID, App Name, and API Key');
      return false;
    }
    return true;
  }

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

  double? _validateNumber(String value) {
    if (value.isEmpty) {
      return null;
    }
    return double.tryParse(value);
  }

  int? _validateDurationNumber(String value) {
    if (value.isEmpty) {
      return null;
    }
    return int.tryParse(value);
  }

  String _getSDKTypeName() {
    switch (sdkType) {
      case FullIdentitySDKType.documentPresent:
        return 'Full Identity (Document Present)';
      case FullIdentitySDKType.docAbsentCustomSelector:
        return 'Doc Absent (Custom Selector)';
      case FullIdentitySDKType.docAbsentIdNumber:
        return 'Doc Absent (ID Number)';
      case FullIdentitySDKType.none:
        return 'None';
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Full Identity Verification Example'),
          backgroundColor: Colors.blue,
        ),
        body: SingleChildScrollView(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              const SizedBox(height: 8),
              // Credentials Section
              const Text(
                'SDK Credentials',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              TextField(
                controller: clientIdController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "Client ID",
                  hintText: "Enter client id",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
                onChanged: (text) => setState(() {}),
              ),
              const SizedBox(height: 14),
              TextField(
                controller: appNameController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "App Name",
                  hintText: "Enter app name",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
                onChanged: (text) => setState(() {}),
              ),
              const SizedBox(height: 14),
              TextField(
                controller: apiKeyController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "API Key",
                  hintText: "Enter api key",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
                onChanged: (text) => setState(() {}),
              ),
              const SizedBox(height: 20),

              // Optional Fields Section
              const Text(
                'Optional Configuration',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              TextField(
                controller: webHookUrlController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "Webhook URL (Optional)",
                  hintText: "Enter webhook url",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
                onChanged: (text) => setState(() {}),
              ),
              const SizedBox(height: 14),
              TextField(
                controller: referenceController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "Reference (Optional)",
                  hintText: "Enter reference",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
                onChanged: (text) => setState(() {}),
              ),
              const SizedBox(height: 14),
              TextField(
                controller: timeoutDurationController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "Timeout Duration (seconds)",
                  hintText:
                      "Enter timeout duration in seconds (Optional, default 120s)",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
                keyboardType: TextInputType.number,
                inputFormatters: [FilteringTextInputFormatter.digitsOnly],
                maxLines: 2,
              ),
              const SizedBox(height: 4),
              CustomCheckbox(
                label: "Point to Development Environment?",
                value: isDev,
                onCheck: (value) => setState(() => isDev = value),
              ),
              const SizedBox(height: 20),

              // Document Absent Specific Options
              const Text(
                'Document Absent Options',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              TextField(
                controller: thresholdController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "Threshold Percent (0-100)",
                  hintText:
                      "Enter threshold for facial comparison 0-100 (Optional)",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
                keyboardType: TextInputType.number,
                inputFormatters: [FilteringTextInputFormatter.digitsOnly],
                maxLines: 2,
              ),
              const SizedBox(height: 14),
              const Text(
                'Liveness Type:',
                style: TextStyle(fontWeight: FontWeight.w500),
              ),
              RadioListTile<LivenessFacialComparisonType>(
                title: const Text('Motional'),
                value: LivenessFacialComparisonType.motional,
                groupValue: selectedLivenessType,
                onChanged: (value) {
                  setState(() {
                    selectedLivenessType = value!;
                  });
                },
              ),
              RadioListTile<LivenessFacialComparisonType>(
                title: const Text('Still'),
                value: LivenessFacialComparisonType.still,
                groupValue: selectedLivenessType,
                onChanged: (value) {
                  setState(() {
                    selectedLivenessType = value!;
                  });
                },
              ),
              const SizedBox(height: 20),

              // Document Type Selection
              const Text(
                'Document Types (for Full Identity & Custom Selector)',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 8),
              ..._buildDocumentTypeCheckboxes(),
              const SizedBox(height: 20),

              // ID Number Specific Section
              const Text(
                'Document Absent with ID Number Options',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              const Text(
                'Select Document Type:',
                style: TextStyle(fontWeight: FontWeight.w500),
              ),
              DropdownButton<DocumentType>(
                value: selectedSingleDocType,
                isExpanded: true,
                items: DocumentType.values.map((DocumentType type) {
                  return DropdownMenuItem<DocumentType>(
                    value: type,
                    child: Text(_getDocumentTypeName(type)),
                  );
                }).toList(),
                onChanged: (DocumentType? newValue) {
                  if (newValue != null) {
                    setState(() {
                      selectedSingleDocType = newValue;
                    });
                  }
                },
              ),
              const SizedBox(height: 14),
              TextField(
                controller: documentNumberController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "Document Number",
                  hintText: "Enter document number (e.g., BVN: 12345678901)",
                  hintStyle: TextStyle(color: Color(0xFFCCCCCC)),
                ),
              ),
              const SizedBox(height: 24),

              // SDK Launch Buttons
              const Text(
                'Launch SDK',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              ElevatedButton(
                onPressed: startFullIdentitySDK,
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.blue,
                  minimumSize: const Size(double.infinity, 50),
                ),
                child: const Text(
                  "Start Full Identity SDK\n(Document Present)",
                  textAlign: TextAlign.center,
                  style: TextStyle(color: Colors.white),
                ),
              ),
              const SizedBox(height: 12),
              ElevatedButton(
                onPressed: startDocAbsentWithCustomSelector,
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.green,
                  minimumSize: const Size(double.infinity, 50),
                ),
                child: const Text(
                  "Start Doc Absent SDK\n(Custom Selector)",
                  textAlign: TextAlign.center,
                  style: TextStyle(color: Colors.white),
                ),
              ),
              const SizedBox(height: 12),
              ElevatedButton(
                onPressed: startDocAbsentWithIdNumber,
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.orange,
                  minimumSize: const Size(double.infinity, 50),
                ),
                child: const Text(
                  "Start Doc Absent SDK\n(ID Number)",
                  textAlign: TextAlign.center,
                  style: TextStyle(color: Colors.white),
                ),
              ),

              // Results Section
              if (sdkType != FullIdentitySDKType.none) ...[
                const SizedBox(height: 34),
                Text(
                  "Result: (${_getSDKTypeName()})",
                  textAlign: TextAlign.center,
                  style: const TextStyle(
                      fontSize: 16, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 12),
                JsonViewerWidget(jsonData: resultResponse?.toJson()),
              ],

              const SizedBox(height: 34),
              const Text(
                'Powered by Blusalt',
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                  color: Colors.blue,
                ),
              ),
              const SizedBox(height: 24),
            ],
          ),
        ),
      ),
    );
  }

  List<Widget> _buildDocumentTypeCheckboxes() {
    return DocumentType.values.map((type) {
      return CustomCheckbox(
        label: _getDocumentTypeName(type),
        value: selectedDocumentTypes.contains(type),
        onCheck: (value) {
          setState(() {
            if (value) {
              if (!selectedDocumentTypes.contains(type)) {
                selectedDocumentTypes.add(type);
              }
            } else {
              selectedDocumentTypes.remove(type);
            }
          });
        },
      );
    }).toList();
  }

  String _getDocumentTypeName(DocumentType type) {
    switch (type) {
      case DocumentType.bvn:
        return 'BVN (Bank Verification Number)';
      case DocumentType.nin:
        return 'NIN (National Identification Number)';
      case DocumentType.passport:
        return 'International Passport';
      case DocumentType.driverLicense:
        return "Driver's License";
      case DocumentType.pvc:
        return "PVC (Permanent Voter's Card)";
    }
  }
}

class CustomCheckbox extends StatelessWidget {
  final String label;
  final bool value;
  final Function(bool) onCheck;

  const CustomCheckbox({
    Key? key,
    required this.label,
    required this.value,
    required this.onCheck,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Checkbox(
          materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
          value: value,
          onChanged: (newValue) => onCheck(newValue ?? false),
        ),
        Expanded(
          child: Text(
            label,
            style: const TextStyle(fontWeight: FontWeight.w500),
          ),
        ),
      ],
    );
  }
}

class JsonViewerWidget extends StatelessWidget {
  final dynamic jsonData;

  const JsonViewerWidget({
    super.key,
    required this.jsonData,
  });

  @override
  Widget build(BuildContext context) {
    final prettyJson = const JsonEncoder.withIndent('  ').convert(jsonData);

    return ClipRRect(
      borderRadius: BorderRadius.circular(8),
      child: Stack(
        children: [
          Container(
            color: const Color(0xFF282C34),
            child: SingleChildScrollView(
              scrollDirection: Axis.vertical,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: ConstrainedBox(
                  constraints: BoxConstraints(
                    minWidth: MediaQuery.of(context).size.width - 32,
                  ),
                  child: Padding(
                    padding: const EdgeInsets.all(16),
                    child: Text(
                      prettyJson,
                      style: const TextStyle(
                        fontFamily: 'monospace',
                        fontSize: 12,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
              ),
            ),
          ),
          Positioned(
            top: 8,
            right: 8,
            child: IconButton(
              icon: const Icon(
                Icons.copy,
                color: Colors.white70,
                size: 20,
              ),
              onPressed: () {
                _copyToClipboard(context, prettyJson);
              },
              tooltip: 'Copy to clipboard',
              constraints: const BoxConstraints(),
              padding: const EdgeInsets.all(8),
            ),
          ),
        ],
      ),
    );
  }

  void _copyToClipboard(BuildContext context, String prettyJson) {
    Clipboard.setData(ClipboardData(text: prettyJson));

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('JSON copied to clipboard'),
        duration: Duration(seconds: 2),
      ),
    );
  }
}
0
likes
0
points
461
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter plugin for Blusalt Full Identity Verification SDK that supports both Android and iOS platforms.

License

unknown (license)

Dependencies

flutter, flutter_web_plugins, plugin_platform_interface

More

Packages that depend on full_identity_verification

Packages that implement full_identity_verification