ITC Card Scanner
A Flutter plugin for scanning credit cards using device camera with OCR capabilities. Automatically extracts card number, expiry date, and cardholder name.
Features
✅ Card Number Detection - Automatically scans and validates card numbers (13-19 digits)
✅ Expiry Date Extraction - Detects multiple formats (MM/YY, MM/YYYY, MM-YY, etc.)
✅ Cardholder Name Recognition - Extracts name from card (optional)
✅ Multiple Card Types - Supports Visa, MasterCard, Amex, Diners, Discover, JCB
✅ Luhn Validation - Built-in card number validation
✅ Real-time Scanning - Live camera preview with scanning overlay
✅ Cross Platform - Works on both Android and iOS
Supported Card Types
- Visa (13, 16, 19 digits)
- MasterCard (16 digits)
- American Express (15 digits)
- Diners Club (14 digits)
- Discover (16 digits)
- JCB (16 digits)
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
itc_card_scanner: ^0.0.4
Then run:
flutter pub get
Platform Setup
Android Setup
1. Minimum SDK Version
Ensure your android/app/build.gradle
has minimum SDK 24:
android {
defaultConfig {
minSdkVersion 24 // Required for camera and ML Kit
targetSdkVersion 34
}
}
2. Permissions (Automatically Added)
The plugin automatically adds these permissions to your AndroidManifest.xml
:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-feature android:name="android.hardware.camera.autofocus" />
iOS Setup
1. Minimum iOS Version
Ensure your ios/Runner.xcodeproj
targets iOS 13.0+:
Open ios/Podfile
and set:
platform :ios, '13.0'
2. Camera Permission (Required)
Add camera permission to ios/Runner/Info.plist
:
<dict>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan credit cards</string>
</dict>
Usage
Basic Usage
import 'package:itc_card_scanner/itc_card_scanner.dart';
// Simple scan with default options
final cardDetails = await CardScanner.scanCard();
if (cardDetails != null) {
print('Card Number: ${cardDetails.cardNumber}');
print('Expiry Date: ${cardDetails.expiryDate}');
print('Cardholder Name: ${cardDetails.cardHolderName}');
print('Card Issuer: ${cardDetails.cardIssuer}');
} else {
print('Scan cancelled or failed');
}
With Custom Options
import 'package:itc_card_scanner/itc_card_scanner.dart';
// Configure scan options
final scanOptions = CardScanOptions(
scanExpiryDate: true, // Scan expiry date (default: true)
scanCardHolderName: true, // Scan cardholder name (default: false)
enableLuhnCheck: true, // Validate card number (default: true)
considerPastDatesInExpiryDateScan: false, // Allow past dates (default: false)
enableDebugLogs: false, // Enable debug logging (default: false)
);
// Scan with custom options
final cardDetails = await CardScanner.scanCard(scanOptions: scanOptions);
if (cardDetails != null) {
print('Scan successful!');
print('Card: ${cardDetails.cardNumber}');
print('Expiry: ${cardDetails.expiryDate}');
print('Name: ${cardDetails.cardHolderName}');
print('Type: ${cardDetails.cardIssuer}');
}
Error Handling
try {
final cardDetails = await CardScanner.scanCard();
if (cardDetails != null) {
// Handle successful scan
handleScannedCard(cardDetails);
} else {
// Handle cancelled scan
print('User cancelled the scan');
}
} catch (e) {
// Handle errors
print('Error scanning card: $e');
}
Complete Example
import 'package:flutter/material.dart';
import 'package:itc_card_scanner/itc_card_scanner.dart';
class CardScanPage extends StatefulWidget {
@override
_CardScanPageState createState() => _CardScanPageState();
}
class _CardScanPageState extends State<CardScanPage> {
CardDetails? _cardDetails;
bool _scanCardHolderName = false;
bool _enableDebugLogs = false;
bool _isLoading = false;
Future<void> _scanCard() async {
setState(() {
_isLoading = true;
});
try {
final scanOptions = CardScanOptions(
scanCardHolderName: _scanCardHolderName,
enableDebugLogs: _enableDebugLogs,
);
final cardDetails = await CardScanner.scanCard(scanOptions: scanOptions);
setState(() {
_cardDetails = cardDetails;
});
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ITC Card Scanner'),
backgroundColor: Colors.blue,
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(
onPressed: _isLoading ? null : _scanCard,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
padding: EdgeInsets.all(16),
),
child: _isLoading
? CircularProgressIndicator(color: Colors.white)
: Text(
'SCAN CARD',
style: TextStyle(fontSize: 18, color: Colors.white),
),
),
SizedBox(height: 20),
// Card Details Display
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Card Details:',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
SizedBox(height: 10),
Text(_cardDetails?.toString() ?? 'No card scanned yet'),
],
),
),
SizedBox(height: 20),
// Options
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
value: _scanCardHolderName,
onChanged: (bool? value) {
setState(() {
_scanCardHolderName = value ?? false;
});
},
),
Text('Scan Card Holder Name'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
value: _enableDebugLogs,
onChanged: (bool? value) {
setState(() {
_enableDebugLogs = value ?? false;
});
},
),
Text('Enable Debug Logs'),
],
),
],
),
),
);
}
}
API Reference
CardScanner
scanCard({CardScanOptions? scanOptions})
Launches the card scanner with optional configuration.
Parameters:
scanOptions
(optional): Configuration options for scanning
Returns: Future<CardDetails?>
- Returns
CardDetails
object on successful scan - Returns
null
if scan was cancelled or failed
CardScanOptions
Configuration class for customizing scan behavior.
const CardScanOptions({
this.scanExpiryDate = true, // Scan expiry date
this.scanCardHolderName = false, // Scan cardholder name
this.initialScansToDrop = 1, // Initial scans to ignore
this.validCardsToScanBeforeFinishingScan = 6, // Scans before finishing
this.considerPastDatesInExpiryDateScan = false, // Allow past dates
this.maxCardHolderNameLength = 26, // Max name length
this.enableLuhnCheck = true, // Enable Luhn validation
this.cardScannerTimeOut = 0, // Timeout (0 = no timeout)
this.enableDebugLogs = false, // Enable debug logging
});
CardDetails
Result object containing scanned card information.
class CardDetails {
final String cardNumber; // Card number without spaces
final String cardHolderName; // Cardholder name (if scanned)
final String expiryDate; // Expiry date in MM/YY format
final String cardIssuer; // Card type/issuer
// Getters
String get cardNumber;
String get cardHolderName;
String get expiryDate;
String get cardIssuer;
// Utility
Map<String, String> get map;
String toString();
}
Supported Date Formats
The scanner can detect expiry dates in multiple formats:
MM/YY
(12/25)MM/YYYY
(12/2025)MM-YY
(12-25)MM-YYYY
(12-2025)MMYY
(1225)MM YY
(12 25)YY/MM
(25/12)YYYY/MM
(2025/12)
All formats are normalized to MM/YY
in the result.
Troubleshooting
Common Issues
Android Issues
-
Camera Permission Denied
- Ensure user grants camera permission when prompted
- Check if device has camera hardware
-
Build Errors
- Verify
minSdkVersion
is 24 or higher - Clean and rebuild:
flutter clean && flutter pub get
- Verify
iOS Issues
-
Camera Permission Denied
- Verify
NSCameraUsageDescription
is in Info.plist - Check iOS Settings → Privacy → Camera
- Verify
-
Build Errors
- Ensure iOS deployment target is 13.0+
- Run
cd ios && pod install
Performance Tips
- Good Lighting: Ensure adequate lighting for better OCR accuracy
- Steady Hand: Hold device steady while scanning
- Card Position: Position card within the overlay frame
- Clean Card: Ensure card surface is clean and readable
Requirements
- Flutter: >= 3.0.0
- Dart: >= 3.0.0
- Android: API level 24+ (Android 7.0+)
- iOS: 13.0+
- Camera: Device must have camera hardware
- Permissions: Camera access required
Privacy & Security
- 🔒 Local Processing: All card data is processed locally on the device
- 🚫 No Network: No card information is transmitted to external servers
- 💾 No Storage: Card data is not stored on the device
- 🛡️ Secure: OCR processing happens in device memory only
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For issues and feature requests, please visit our GitHub repository.