vornid 1.2.4 copy "vornid: ^1.2.4" to clipboard
vornid: ^1.2.4 copied to clipboard

VornID identity verification SDK — capture-and-transport client for biometric liveness detection, image analysis, and identity verification.

example/lib/main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import 'package:vornid/vornid.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

/// Demonstrates the recommended `MaterialApp` integration path: register a
/// `VornIdThemeExtension` on both light and dark `ThemeData` so the SDK
/// automatically follows the host app's brightness, brand color, and font.
///
/// Per-session and per-init overrides are also available — see the demo page
/// for examples using `VornIdConfig.theme` and `LivenessOptions.theme`.
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    const brand = Color(0xFF1F3A8A);
    return MaterialApp(
      title: 'VornID SDK Demo',
      theme: ThemeData(
        colorSchemeSeed: brand,
        useMaterial3: true,
        brightness: Brightness.light,
        // Set a fontFamily here to flow into VornIdTypography via
        // VornIdTheme.fromMaterialTheme — no font assets need to ship with
        // the SDK package.
        // fontFamily: 'Inter',
        extensions: const [VornIdThemeExtension()],
      ),
      darkTheme: ThemeData(
        colorSchemeSeed: brand,
        useMaterial3: true,
        brightness: Brightness.dark,
        // fontFamily: 'Inter',
        extensions: const [VornIdThemeExtension()],
      ),
      themeMode: ThemeMode.system,
      home: const DemoPage(),
    );
  }
}

/// Theming options the demo can apply to a single liveness session.
enum _ThemeChoice {
  inheritFromMaterialApp,
  explicitDark,
  explicitLight,
  brandedDark,
}

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

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  /// Keep in sync with [LivenessOptions.showResultScreen]: when `false`, the SDK
  /// shows only a generic submitted overlay, and this demo must not reveal
  /// scores on the home screen after [onComplete] either.
  static const bool _showDetailedLivenessResult = false;

  VornIdSdk? _sdk;
  bool _initializing = true;
  String _status = 'Initializing SDK...';
  Color _statusColor = Colors.grey;
  LivenessResult? _lastResult;

  _ThemeChoice _themeChoice = _ThemeChoice.inheritFromMaterialApp;

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

  Future<void> _initSdk() async {
    try {
      final sdk = await VornIdSdk.initialize(
        const VornIdConfig(
          apiKey: 'stg_sbox_V2tAm1vDyQUBnK*************',
          environment: VornIdEnvironment.sandbox,
          // Optional: set a global SDK default theme here. Per-session
          // LivenessOptions.theme still wins. When null (default), the SDK
          // inherits from the surrounding MaterialApp.
          // theme: VornIdTheme.dark(),
        ),
      );
      if (!mounted) return;
      setState(() {
        _sdk = sdk;
        _initializing = false;
        _status = 'SDK initialized (sandbox)';
        _statusColor = Colors.green;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _initializing = false;
        _status = 'Init failed: $e';
        _statusColor = Colors.red;
      });
    }
  }

  VornIdTheme? _themeFor(_ThemeChoice choice) {
    switch (choice) {
      case _ThemeChoice.inheritFromMaterialApp:
        return null; // Resolved via MaterialApp -> VornIdThemeExtension.
      case _ThemeChoice.explicitDark:
        return const VornIdTheme.dark();
      case _ThemeChoice.explicitLight:
        return const VornIdTheme.light();
      case _ThemeChoice.brandedDark:
        const base = VornIdTheme.dark();
        return base.copyWith(
          colors: base.colors.copyWith(
            accent: const Color(0xFFFF6B00),
            onAccent: Colors.white,
          ),
        );
    }
  }

  void _startLivenessCheck() {
    if (_sdk == null) return;
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => VornIdLivenessScreen(
          sdk: _sdk!,
          options: LivenessOptions(
            actionType: LivenessActionType.registration,
            showPreview: true,
            facing: CameraFacing.front,
            mode: LivenessMode.active,
            enableCooldownOnFailure: false,
            showResultScreen: _showDetailedLivenessResult,
            theme: _themeFor(_themeChoice),
          ),
          onComplete: (result) {
            Navigator.of(context).pop();
            debugPrint('result: $result');
            setState(() {
              _lastResult = result;
              _statusColor = Colors.green;
              _status = 'Verification submitted';
            });
          },
          onError: (error) {
            Navigator.of(context).pop();
            setState(() {
              _lastResult = null;
              _statusColor = Colors.orange;
              _status = '${error.code}: ${error.message}';
            });
          },
        ),
      ),
    );
  }

  @override
  void dispose() {
    _sdk?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      appBar: AppBar(title: const Text('VornID SDK Demo'), centerTitle: true),
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24),
          child: Column(
            children: [
              const SizedBox(height: 24),
              Icon(
                Icons.fingerprint,
                size: 72,
                color: theme.colorScheme.primary,
              ),
              const SizedBox(height: 32),
              _buildStatusCard(),
              if (_lastResult != null &&
                  kDebugMode &&
                  _showDetailedLivenessResult) ...[
                const SizedBox(height: 20),
                _buildScoreBreakdown(),
              ],
              const SizedBox(height: 24),
              _buildThemePicker(),
              const SizedBox(height: 24),
              SizedBox(
                width: double.infinity,
                height: 56,
                child: FilledButton.icon(
                  onPressed: _sdk != null && !_initializing
                      ? _startLivenessCheck
                      : null,
                  icon: const Icon(Icons.face, size: 28),
                  label: const Text(
                    'Start Liveness Check',
                    style: TextStyle(fontSize: 18),
                  ),
                ),
              ),
              const SizedBox(height: 16),
              Text(
                'VornID SDK v${VornIdConstants.sdkVersion}',
                style: TextStyle(
                  fontSize: 12,
                  color: theme.colorScheme.outline,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildThemePicker() {
    final theme = Theme.of(context);
    String label(_ThemeChoice c) => switch (c) {
          _ThemeChoice.inheritFromMaterialApp =>
            'Inherit from MaterialApp (recommended)',
          _ThemeChoice.explicitDark => 'Explicit dark preset',
          _ThemeChoice.explicitLight => 'Explicit light preset',
          _ThemeChoice.brandedDark => 'Dark preset with branded accent',
        };
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: theme.dividerColor),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            'Theme for next session',
            style: TextStyle(
              fontWeight: FontWeight.w600,
              color: theme.colorScheme.onSurface,
            ),
          ),
          const SizedBox(height: 8),
          for (final choice in _ThemeChoice.values)
            RadioListTile<_ThemeChoice>(
              dense: true,
              contentPadding: EdgeInsets.zero,
              value: choice,
              groupValue: _themeChoice,
              onChanged: (v) {
                if (v != null) setState(() => _themeChoice = v);
              },
              title: Text(label(choice)),
            ),
        ],
      ),
    );
  }

  Widget _buildStatusCard() {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: _statusColor.withValues(alpha: 0.1),
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: _statusColor.withValues(alpha: 0.3)),
      ),
      child: _initializing
          ? const Column(
              children: [
                CircularProgressIndicator(),
                SizedBox(height: 16),
                Text(
                  'Initializing SDK...',
                  style: TextStyle(fontSize: 16),
                  textAlign: TextAlign.center,
                ),
              ],
            )
          : Row(
              children: [
                Icon(
                  _lastResult != null
                      ? Icons.check_circle
                      : (_sdk != null
                            ? Icons.check_circle_outline
                            : Icons.error_outline),
                  color: _statusColor,
                  size: 28,
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: Text(
                    _status,
                    style: TextStyle(
                      fontSize: 16,
                      fontWeight: FontWeight.w500,
                      color: _statusColor,
                    ),
                  ),
                ),
              ],
            ),
    );
  }

  Widget _buildScoreBreakdown() {
    final scores = _lastResult!.scoreBreakdown;
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surfaceContainerHighest,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            'Score Breakdown',
            style: TextStyle(
              fontSize: 16,
              fontWeight: FontWeight.w600,
              color: Theme.of(context).colorScheme.onSurface,
            ),
          ),
          const SizedBox(height: 16),
          _ScoreItem(
            label: 'Liveness Confidence',
            value: scores.livenessConfidence,
          ),
          const SizedBox(height: 10),
          _ScoreItem(
            label: 'Spoof Likelihood',
            value: scores.spoofLikelihood,
            inverted: true,
          ),
          const SizedBox(height: 10),
          _ScoreItem(label: 'Quality Score', value: scores.qualityScore),
          const SizedBox(height: 10),
          _ScoreItem(
            label: 'Fraud Risk Signal',
            value: scores.fraudRiskSignal,
            inverted: true,
          ),
          const SizedBox(height: 12),
          Divider(color: Theme.of(context).dividerColor),
          const SizedBox(height: 8),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                'Model',
                style: TextStyle(
                  fontSize: 13,
                  color: Theme.of(context).colorScheme.outline,
                ),
              ),
              Text(
                _lastResult!.modelVersion.isNotEmpty
                    ? _lastResult!.modelVersion
                    : 'N/A',
                style: TextStyle(
                  fontSize: 13,
                  color: Theme.of(context).colorScheme.outline,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class _ScoreItem extends StatelessWidget {
  const _ScoreItem({
    required this.label,
    required this.value,
    this.inverted = false,
  });

  final String label;
  final double value;
  final bool inverted;

  Color _color() {
    final v = inverted ? 1.0 - value : value;
    if (v >= 0.7) return Colors.green;
    if (v >= 0.4) return Colors.amber.shade700;
    return Colors.red;
  }

  @override
  Widget build(BuildContext context) {
    final displayPercent = (value.clamp(0.0, 1.0) * 100).round();
    return Row(
      children: [
        Expanded(
          child: Text(
            label,
            style: TextStyle(
              fontSize: 14,
              color: Theme.of(context).colorScheme.onSurfaceVariant,
            ),
          ),
        ),
        Text(
          '$displayPercent%',
          style: TextStyle(
            fontSize: 15,
            fontWeight: FontWeight.w600,
            color: _color(),
          ),
        ),
        const SizedBox(width: 8),
        SizedBox(
          width: 60,
          child: ClipRRect(
            borderRadius: BorderRadius.circular(4),
            child: LinearProgressIndicator(
              value: value.clamp(0.0, 1.0),
              minHeight: 6,
              color: _color(),
              backgroundColor: _color().withValues(alpha: 0.15),
            ),
          ),
        ),
      ],
    );
  }
}
0
likes
0
points
705
downloads

Publisher

verified publishervornid.com

Weekly Downloads

VornID identity verification SDK — capture-and-transport client for biometric liveness detection, image analysis, and identity verification.

Homepage

License

unknown (license)

Dependencies

camera, crypto, device_info_plus, dio, flutter, flutter_bloc, flutter_rust_bridge, freezed_annotation, json_annotation, lottie, package_info_plus, permission_handler, plugin_platform_interface, pointycastle, safe_device, screen_brightness, shared_preferences, uuid

More

Packages that depend on vornid

Packages that implement vornid