full_identity_verification 0.1.11 copy "full_identity_verification: ^0.1.11" to clipboard
full_identity_verification: ^0.1.11 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: '');
  TextEditingController appNameController = TextEditingController(text: '');
  TextEditingController apiKeyController = TextEditingController(text: '');
  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
125
points
406
downloads

Publisher

unverified uploader

Weekly Downloads

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

Homepage

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter, flutter_web_plugins, plugin_platform_interface

More

Packages that depend on full_identity_verification

Packages that implement full_identity_verification