naya_id 0.1.1
naya_id: ^0.1.1 copied to clipboard
SDK Flutter officiel de Naya ID — vérification d'identité (eKYC) clé en main pour l'Afrique de l'Ouest. Capture de document, OCR, selfie avec liveness on-device et décision, dans une UI complète prête [...]
import 'package:flutter/material.dart';
import 'package:naya_id/naya_id.dart';
void main() => runApp(const NayaExampleApp());
class NayaExampleApp extends StatelessWidget {
const NayaExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'Naya ID — Exemple',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1A3A5C)),
),
home: const DemoHomePage(),
);
}
class DemoHomePage extends StatefulWidget {
const DemoHomePage({super.key});
@override
State<DemoHomePage> createState() => _DemoHomePageState();
}
class _DemoHomePageState extends State<DemoHomePage> {
// Injectée au lancement : flutter run --dart-define=NAYA_API_KEY=naya_…
// (évite de committer une clé ou de la saisir sur le téléphone)
final _apiKeyController = TextEditingController(
text: const String.fromEnvironment('NAYA_API_KEY'),
);
NayaResult? _result;
bool _running = false;
Future<void> _startVerification() async {
final apiKey = _apiKeyController.text.trim();
if (apiKey.isEmpty) return;
setState(() => _running = true);
final result = await NayaId.startVerification(
context,
apiKey: apiKey,
config: const NayaConfig(
country: NayaCountry.bj,
locale: NayaLocale.fr,
// callbackUrl: 'https://api.monclient.com/kyc/callback',
),
);
setState(() {
_result = result;
_running = false;
});
}
@override
void dispose() {
_apiKeyController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final result = _result;
return Scaffold(
appBar: AppBar(title: const Text('Naya ID — App de démonstration')),
body: ListView(
padding: const EdgeInsets.all(20),
children: [
TextField(
controller: _apiKeyController,
decoration: const InputDecoration(
labelText: 'Clé API (naya_test_… ou naya_live_…)',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
FilledButton(
onPressed: _running ? null : _startVerification,
child: const Text('Lancer la vérification'),
),
if (result != null) ...[
const SizedBox(height: 28),
_Section(
title: 'Vérification',
children: [
_ResultTile('Statut', result.status.name),
if (result.verificationId != null)
_ResultTile('Référence', result.verificationId!, mono: true),
if (result.riskScore != null)
_ResultTile('Score de risque', '${result.riskScore} / 100'),
if (result.matchScore != null)
_ResultTile('Match facial', '${result.matchScore} / 100'),
if (result.authenticityScore != null)
_ResultTile(
'Authenticité',
'${result.authenticityScore} / 100',
),
if (result.livenessScore != null)
_ResultTile('Liveness', '${result.livenessScore} / 100'),
if (result.ocrConfidence != null)
_ResultTile('Confiance OCR', '${result.ocrConfidence} / 100'),
if (result.note != null) _ResultTile('Note', result.note!),
if (result.errorMessage != null)
_ResultTile('Erreur', result.errorMessage!),
],
),
if (result.ocrData != null) ...[
const SizedBox(height: 20),
_Section(
title: 'Données extraites du document',
subtitle:
"C'est ici que votre application afficherait son écran de "
'correction, à partir de result.ocrData.',
children: _ocrTiles(result.ocrData!),
),
],
],
],
),
);
}
List<Widget> _ocrTiles(NayaOcrData ocr) {
final fields = <String, String?>{
'Nom': ocr.lastName,
'Prénoms': ocr.firstName,
'Date de naissance': ocr.dateOfBirth,
'Lieu de naissance': ocr.placeOfBirth,
'Sexe': ocr.gender,
'Nationalité': ocr.nationality,
'NPI': ocr.npi,
'N° de pièce': ocr.documentNumber,
'Expiration': ocr.expirationDate,
'Pays émetteur': ocr.issuingCountry,
'Autorité': ocr.issuingAuthority,
};
final tiles = [
for (final entry in fields.entries)
if (entry.value != null && entry.value!.isNotEmpty)
_ResultTile(entry.key, entry.value!),
];
return tiles.isEmpty
? const [_ResultTile('—', 'Aucune donnée extraite')]
: tiles;
}
}
class _Section extends StatelessWidget {
const _Section({required this.title, required this.children, this.subtitle});
final String title;
final String? subtitle;
final List<Widget> children;
@override
Widget build(BuildContext context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: Theme.of(context).textTheme.titleMedium),
if (subtitle != null) ...[
const SizedBox(height: 4),
Text(
subtitle!,
style: const TextStyle(fontSize: 12, color: Colors.black54),
),
],
const SizedBox(height: 10),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE5E7EB)),
),
child: Column(children: children),
),
],
);
}
class _ResultTile extends StatelessWidget {
const _ResultTile(this.label, this.value, {this.mono = false});
final String label;
final String value;
final bool mono;
@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 130,
child: Text(
label,
style: const TextStyle(color: Colors.black54, fontSize: 13),
),
),
Expanded(
child: SelectableText(
value,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 13,
fontFamily: mono ? 'monospace' : null,
),
),
),
],
),
);
}