Quick Start

Use this guide to get the Verifier SDK rendering a verification flow in your Flutter application.

Complete Installation First

Make sure you have installed the SDK before following this guide. See the Installation page. If your app uses NFC delivery of the verification request, also complete the Permissions setup before testing on device.

Widget-Based Integration (Recommended)

Use VerificationView for the full verification flow with built-in UI:

import 'package:vecu_verifier_flutter_sdk/vecu_verifier_flutter_sdk.dart';

final sdk = createVerifierSDK(VerifierConfig(
  stage: DeploymentStage.sandbox,
  bearerToken: 'your-vecu-api-token',
));

class VerifyScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return VerificationView(
      sdk: sdk,
      vin: '1HGBH41JXMN109186',
      proximity: const ProximityConfig(thresholdMiles: 5.0),
      onApproved: (result) {
        print('Verified! ${result.requestId}');
        print('Proximity: ${result.proximityCheck?.status}');
        // Navigate to your app's approved screen here
      },
      onDenied: (result) {
        print('DO NOT RELEASE ${result.requestId}');
        // Navigate to your app's denied screen here
      },
      onExpired: (result) {
        print('Session expired');
        // Navigate to your app's expired screen here
      },
      onError: (error) {
        print('${error.code}: ${error.message}');
      },
    );
  }
}

VerificationView handles all states: loading, QR code display with countdown timer, approved (with proximity pass/fail/unchecked), denied, expired, and error.

Callbacks vs SDK Events

When you use VerificationView, the recommended integration point is the callback props:

  • onApproved
  • onDenied
  • onExpired
  • onError

These are plain functions your widget passes into the component. Internally, the SDK listens to sdk.on<StatusChangedEvent>(...) and invokes your callbacks when a terminal status arrives.

For full UI control, use VerificationController directly — see the headless example in the SDK repository.

Headless Integration

VerificationController is a ChangeNotifier-based orchestrator for host apps that want to build their own UI while the controller manages API calls, polling, and state transitions:

final controller = VerificationController(
  sdk: sdk,
  vin: '1HGBH41JXMN109186',
  proximity: const ProximityConfig(thresholdMiles: 5.0),
  onApproved: (result) => print('Verified! ${result.requestId}'),
  onDenied: (result) => print('DO NOT RELEASE'),
  onExpired: (_) => print('Expired'),
  onError: (error) => print('Error: ${error.displayCode}'),
);

Build your UI with ListenableBuilder and pattern matching on controller.state:

ListenableBuilder(
  listenable: controller,
  builder: (context, _) {
    return switch (controller.state) {
      LoadingState() => const CircularProgressIndicator(),
      QRState(:final qrContent, :final expiresAt) =>
        MyQRWidget(qrContent, expiresAt),
      ApprovedState(:final result) => MyApprovedWidget(result),
      DeniedState(:final result, :final vin) => MyDeniedWidget(result, vin),
      ExpiredState() => const Text('Session expired'),
      QRExpiredState() => TextButton(
          onPressed: controller.restart,
          child: const Text('QR Expired — Retry'),
        ),
      ErrorState(:final error) => Text('Error: ${error.userMessage}'),
    };
  },
)

Clean up when done:

controller.dispose();
sdk.destroy();

Next Steps