nadra_verisys_flutter 0.1.2
nadra_verisys_flutter: ^0.1.2 copied to clipboard
A Flutter package for integrating NADRA Verisys API for Pakistani CNIC verification, biometric validation, and KYC compliance. Perfect for fintech, banking, and identity verification apps.
import 'package:flutter/material.dart';
import 'package:nadra_verisys_flutter/nadra_verisys_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'NADRA Verisys Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
useMaterial3: true,
),
home: const VerificationScreen(),
);
}
}
class VerificationScreen extends StatefulWidget {
const VerificationScreen({super.key});
@override
State<VerificationScreen> createState() => _VerificationScreenState();
}
class _VerificationScreenState extends State<VerificationScreen> {
final _formKey = GlobalKey<FormState>();
final _cnicController = TextEditingController();
final _nameController = TextEditingController();
final _dobController = TextEditingController();
bool _isLoading = false;
VerificationResponse? _response;
String? _errorMessage;
// Initialize Verisys client
late final VerisysClient _client;
@override
void initState() {
super.initState();
_client = VerisysClient(
apiKey: 'your-api-key-here', // Replace with API key from NADRA
useSandbox: true, // Use sandbox environment (requires NADRA sandbox credentials)
);
}
@override
void dispose() {
_cnicController.dispose();
_nameController.dispose();
_dobController.dispose();
_client.dispose();
super.dispose();
}
Future<void> _verifyCnic() async {
if (!_formKey.currentState!.validate()) {
return;
}
setState(() {
_isLoading = true;
_errorMessage = null;
_response = null;
});
try {
final request = VerificationRequest(
cnicNumber: _cnicController.text.replaceAll('-', ''),
fullName: _nameController.text.isNotEmpty ? _nameController.text : null,
dateOfBirth: _dobController.text.isNotEmpty ? _dobController.text : null,
);
final response = await _client.verifyCnic(request);
setState(() {
_response = response;
_isLoading = false;
});
} on VerisysException catch (e) {
setState(() {
_errorMessage = e.message;
_isLoading = false;
});
} catch (e) {
setState(() {
_errorMessage = 'Unexpected error: $e';
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('NADRA CNIC Verification'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Info card
Card(
color: Colors.blue[50],
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.info_outline, color: Colors.blue[700]),
const SizedBox(width: 8),
Text(
'Important Note',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue[700],
),
),
],
),
const SizedBox(height: 8),
const Text('• Requires NADRA API credentials'),
const Text('• Test CNICs provided by NADRA'),
const Text('• Contact NADRA for access'),
],
),
),
),
const SizedBox(height: 24),
// CNIC Input
TextFormField(
controller: _cnicController,
decoration: const InputDecoration(
labelText: 'CNIC Number',
hintText: '12345-6789012-3 or 1234567890123',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.credit_card),
),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter CNIC number';
}
final cleanCnic = value.replaceAll('-', '');
if (cleanCnic.length != 13) {
return 'CNIC must be 13 digits';
}
if (!RegExp(r'^\d+$').hasMatch(cleanCnic)) {
return 'CNIC must contain only digits';
}
return null;
},
),
const SizedBox(height: 16),
// Name Input (Optional)
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Full Name (Optional)',
hintText: 'Muhammad Ali',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
),
const SizedBox(height: 16),
// Date of Birth (Optional)
TextFormField(
controller: _dobController,
decoration: const InputDecoration(
labelText: 'Date of Birth (Optional)',
hintText: 'YYYY-MM-DD',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.calendar_today),
),
validator: (value) {
if (value != null && value.isNotEmpty) {
if (!RegExp(r'^\d{4}-\d{2}-\d{2}$').hasMatch(value)) {
return 'Use format: YYYY-MM-DD';
}
}
return null;
},
),
const SizedBox(height: 24),
// Verify Button
ElevatedButton(
onPressed: _isLoading ? null : _verifyCnic,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text(
'Verify CNIC',
style: TextStyle(fontSize: 16),
),
),
const SizedBox(height: 24),
// Error Message
if (_errorMessage != null)
Card(
color: Colors.red[50],
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Icon(Icons.error_outline, color: Colors.red[700]),
const SizedBox(width: 8),
Expanded(
child: Text(
_errorMessage!,
style: TextStyle(color: Colors.red[700]),
),
),
],
),
),
),
// Response
if (_response != null) ...[
Card(
color: _response!.isVerified
? Colors.green[50]
: Colors.orange[50],
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
_response!.isVerified
? Icons.check_circle_outline
: Icons.warning_amber,
color: _response!.isVerified
? Colors.green[700]
: Colors.orange[700],
),
const SizedBox(width: 8),
Text(
_response!.isVerified
? 'Verification Successful'
: 'Verification Failed',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: _response!.isVerified
? Colors.green[700]
: Colors.orange[700],
),
),
],
),
const SizedBox(height: 12),
Text(
'Status: ${_response!.status.name}',
style: const TextStyle(fontWeight: FontWeight.w500),
),
Text('Message: ${_response!.message}'),
if (_response!.transactionId != null)
Text('Transaction ID: ${_response!.transactionId}'),
if (_response!.citizenData != null) ...[
const Divider(height: 24),
const Text(
'Citizen Information',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
const SizedBox(height: 8),
_buildInfoRow('CNIC',
_response!.citizenData!.cnicNumber),
_buildInfoRow(
'Full Name', _response!.citizenData!.fullName),
if (_response!.citizenData!.fatherName != null)
_buildInfoRow('Father Name',
_response!.citizenData!.fatherName!),
if (_response!.citizenData!.dateOfBirth != null)
_buildInfoRow('Date of Birth',
_response!.citizenData!.dateOfBirth!),
if (_response!.citizenData!.gender != null)
_buildInfoRow(
'Gender', _response!.citizenData!.gender!),
if (_response!.citizenData!.presentAddress != null)
_buildInfoRow('Address',
_response!.citizenData!.presentAddress!),
],
],
),
),
),
],
],
),
),
),
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 120,
child: Text(
'$label:',
style: const TextStyle(fontWeight: FontWeight.w500),
),
),
Expanded(
child: Text(value),
),
],
),
);
}
}