**What is KYC and Why Does it Matter?
**KYC is a mandatory regulatory process that financial institutions use to verify the identity of their customers. In digital banking, this means:
Verifying government-issued identity documents (NIC, Passport)
Biometric face matching
Liveness detection to prevent spoofing
Real-time database checks against government records
A poorly implemented KYC system can lead to regulatory fines, fraud, and data breaches. Getting it right is critical.
The Core Architecture
After building KYC systems for multiple banking apps, I settled on a Clean Architecture approach that separates concerns clearly:
Presentation Layer → Bloc/Riverpod
Domain Layer → Use Cases + Entities
Data Layer → Repositories + Remote/Local Sources
This separation means:
KYC business logic stays independent of UI
Easy to swap SDKs without touching business logic
Testable in isolation — critical for regulated environments
Key Component 1 — NIC Scanning
abstract class NicScanRepository {
Future> scanNic();
}
class NicScanRepositoryImpl implements NicScanRepository {
final NicScanDataSource dataSource;
NicScanRepositoryImpl({required this.dataSource});
@override
Future> scanNic() async {
try {
final result = await dataSource.initiateScan();
return Right(result.toDomain());
} on ScanException catch (e) {
return Left(ScanFailure(message: e.message));
}
}
}
Key Component 2 — Biometric Authentication with IdWise SDK
class BiometricKycDataSource {
Future initiateKyc({
required String journeyDefinitionId,
required String referenceNumber,
}) {
final completer = Completer();
IdWise.initialize(
clientKey: AppConfig.idWiseClientKey,
environment: IdWiseEnvironment.production,
);
IdWise.startJourney(
journeyDefinitionId: journeyDefinitionId,
referenceNumber: referenceNumber,
locale: 'en',
delegate: _KycDelegate(completer),
);
return completer.future;
}
}
class _KycDelegate implements IdWiseSDKDelegate {
final Completer completer;
_KycDelegate(this.completer);
@override
void onJourneyCompleted(String journeyId, bool isCompleted) {
completer.complete(KycResult(
journeyId: journeyId,
status: isCompleted ? KycStatus.completed : KycStatus.failed,
));
}
@override
void onJourneyResumed(String journeyId) {}
@override
void onError(IdWiseError error) {
completer.completeError(KycException(message: error.message));
}
}
Key Component 3 — State Management for KYC Flow
enum KycStep { idle, scanning, biometric, verifying, completed, failed }
@riverpod
class KycNotifier extends _$KycNotifier {
@override
KycState build() => const KycState(step: KycStep.idle);
Future startNicScan() async {
state = state.copyWith(step: KycStep.scanning);
final result = await ref.read(nicScanRepositoryProvider).scanNic();
result.fold(
(failure) => state = state.copyWith(
step: KycStep.failed,
errorMessage: failure.message,
),
(nicData) => state = state.copyWith(
step: KycStep.biometric,
nicData: nicData,
),
);
}
Future startBiometric() async {
state = state.copyWith(step: KycStep.verifying);
final result = await ref.read(biometricKycRepositoryProvider)
.initiateKyc(referenceNumber: state.nicData!.nicNumber);
result.fold(
(failure) => state = state.copyWith(
step: KycStep.failed,
errorMessage: failure.message,
),
(kycResult) => state = state.copyWith(
step: KycStep.completed,
kycResult: kycResult,
),
);
}
}
Lessons Learned
After building KYC for multiple production banking apps, here is what I wish I had known earlier:
Abstract your SDK dependencies early — KYC SDK providers change, merge, and update APIs frequently
Test on real low-end devices — camera performance varies dramatically across the Android ecosystem
Handle network failures gracefully — KYC mid-flow network drops are common; build resumable journeys
Log anonymously — never log NIC numbers or biometric data, even in development
Design for accessibility — banking apps serve all demographics; KYC flows must work for elderly users too
Conclusion
Building secure KYC systems in Flutter for production banking applications requires a combination of clean architecture, robust state management, and security-first thinking. The patterns described here have been battle-tested across multiple live digital banking applications serving real users in regulated financial environments.
If you are building a fintech app and have questions about KYC implementation, feel free to connect — I am always happy to discuss secure mobile architecture.
Top comments (0)