Integration Guide
A reference architecture for building a verifier app using the Verifier Flutter SDK. This guide shows which screens your app owns, which the SDK renders, and how they connect.
Thin Client Pattern
The verifier app is intentionally thin. Your app handles navigation, authentication, and VIN acquisition. The SDK handles all verification UI, NFC/BLE communication, and backend polling. Most screens are just wrappers around an SDK widget with navigation callbacks.
App Architecture
MaterialApp
├── Auth (your auth layer)
└── MaterialApp.routes
├── HomeScreen ← App (entry point)
├── CameraScanScreen ← App (VIN acquisition)
├── ManualEntryScreen ← App (VIN acquisition)
└── VerificationScreen ← App wrapper → SDK VerificationView
Screen Responsibilities
Screens Your App Owns (100% app code)
These screens have no SDK widgets — your app renders all UI and handles all logic.
| Screen | Purpose | Navigates To |
|---|---|---|
| HomeScreen | Action cards for different verification methods | CameraScan, ManualEntry |
| CameraScanScreen | Live camera OCR to detect VIN from vehicle dashboard | Verification (with detected VIN) |
| ManualEntryScreen | 17-character VIN text input with validation | Verification (with entered VIN) |
Screens That Wrap SDK Widgets
These screens are thin wrappers — typically under 80 lines. Your app
provides a Scaffold, creates the SDK instance, and wires up navigation
callbacks. The SDK renders all visible UI.
| Screen | SDK Widget | What the SDK Renders |
|---|---|---|
| VerificationScreen | VerificationView | QR code, countdown timer, NFC delivery badge, and built-in result states. VerificationController for custom UI. |
SDK Lifecycle
The Flutter SDK uses a factory function rather than a provider. Each screen creates an SDK instance when it mounts and destroys it when it unmounts:
import 'package:vecu_verifier_flutter_sdk/vecu_verifier_flutter_sdk.dart';
class VerificationScreen extends StatefulWidget {
@override
State<VerificationScreen> createState() => _VerificationScreenState();
}
class _VerificationScreenState extends State<VerificationScreen> {
late final VerifierSDK _sdk;
@override
void initState() {
super.initState();
_sdk = createVerifierSDK(VerifierConfig(
stage: DeploymentStage.production,
bearerToken: context.read<Auth>().token,
));
}
@override
void dispose() {
_sdk.destroy();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
body: VerificationView(sdk: _sdk, vin: widget.vin, /* callbacks */),
);
}
If your app prefers a shared instance, create the SDK in a parent widget or use your app's DI solution (Riverpod, get_it, Provider) and pass it down. The SDK object is cheap — creating it per-screen is fine for most apps.
Writing a Wrapper Screen
A typical wrapper screen follows this pattern:
class VerificationScreen extends StatefulWidget {
final String vin;
const VerificationScreen({required this.vin, super.key});
@override
State<VerificationScreen> createState() => _VerificationScreenState();
}
class _VerificationScreenState extends State<VerificationScreen> {
late final VerifierSDK _sdk;
@override
void initState() {
super.initState();
_sdk = createVerifierSDK(_config);
}
@override
void dispose() {
_sdk.destroy();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Verify Credentials')),
body: VerificationView(
sdk: _sdk,
vin: widget.vin,
proximity: const ProximityConfig(thresholdMiles: 5.0),
// SDK says "what happened", app decides "where to go"
onApproved: (result) {
Navigator.pushReplacementNamed(context, '/result', arguments: {
'outcome': 'approved',
'vin': result.verifiedClaims?.vin,
});
},
onDenied: (result) {
Navigator.pushReplacementNamed(context, '/result',
arguments: {'outcome': 'denied'});
},
onExpired: (_) => Navigator.pop(context),
onError: (error) => ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(error.userMessage)),
),
),
);
}
}
The SDK has zero knowledge of your navigation structure. It communicates via callbacks — your app decides where to navigate.
Callback-Based Integration
For screen-based integrations, use the callback props as your app boundary:
onApprovedonDeniedonExpiredonError
These are plain functions that your wrapper screen passes into
VerificationView. Internally, the SDK listens to its own event emitter
and then invokes those callbacks for you.
Verification Flow
HomeScreen
├── CameraScanScreen ──► VIN ──► VerificationScreen (SDK: QR + polling)
└── ManualEntryScreen ──► VIN ──► VerificationScreen
Your app's job: Acquire the VIN (camera, keyboard, or your own source). SDK's job: Create presentation request, display QR, poll for result.
What Your App Does NOT Need to Manage
The SDK handles all of the following internally:
- Presentation request creation and QR code generation
- Verification status polling and timeout
- All in-flow UI states (loading, QR, waiting, approved, denied, expired, error)
- Optional NFC delivery of the request via Android HCE (see Permissions)
Next Steps
- Overview — Quick start with
VerificationView - Permissions — Native setup for NFC delivery and entitlements
- API Reference — Full SDK configuration, widgets, events, and types