id_card_ocr_web 1.1.0
id_card_ocr_web: ^1.1.0 copied to clipboard
A client-side OCR package for Flutter Web using Tesseract.js.
example/lib/main.dart
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:id_card_ocr_web/id_card_ocr_web.dart';
import 'package:image_picker/image_picker.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
IdCardOcr.initialize();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: OcrDashboard());
}
}
class OcrDashboard extends StatefulWidget {
const OcrDashboard({super.key});
@override
State<OcrDashboard> createState() => _OcrDashboardState();
}
class _OcrDashboardState extends State<OcrDashboard> {
String _status = 'Idle';
Map<String, dynamic>? _extractedData;
Uint8List? _selectedImageBytes;
final ImagePicker _picker = ImagePicker();
Future<void> _pickAndProcessImage(ImageSource source, bool isKtp) async {
setState(() {
_status = 'Opening Media Channel...';
_extractedData = null;
});
try {
final XFile? file = await _picker.pickImage(source: source, imageQuality: 100);
if (file == null) {
setState(() => _status = 'Selection cancelled by user.');
return;
}
final Uint8List bytes = await file.readAsBytes();
setState(() {
_selectedImageBytes = bytes;
_status = 'Processing Card via Client Engine...';
});
if (isKtp) {
final KtpResult result = await IdCardOcr.processKtp(bytes);
setState(() {
_status = result.isSuccess ? 'Success' : 'Extraction Failed';
_extractedData = result.toJson();
});
}
} catch (e) {
setState(() => _status = 'Execution Error: $e');
}
}
void _showSourceSelectionSheet(bool isKtp) {
showModalBottomSheet(
context: context,
builder: (context) => SafeArea(
child: Wrap(
children: [
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Choose from Gallery'),
onTap: () {
Navigator.pop(context);
_pickAndProcessImage(ImageSource.gallery, isKtp);
},
),
ListTile(
leading: const Icon(Icons.camera_alt),
title: const Text('Take Photo / Capture Camera'),
onTap: () {
Navigator.pop(context);
_pickAndProcessImage(ImageSource.camera, isKtp);
},
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ID Card OCR Web Proof-of-Concept')),
body: Padding(
padding: const EdgeInsets.all(24.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Engine Status: $_status', style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 20),
Row(
children: [
ElevatedButton.icon(
onPressed: () => _showSourceSelectionSheet(true),
icon: const Icon(Icons.credit_card),
label: const Text('Scan KTP (ID)'),
),
const SizedBox(width: 12),
ElevatedButton.icon(
onPressed: () => _showSourceSelectionSheet(false),
icon: const Icon(Icons.badge),
label: const Text('Scan MyKad (MY) (Soon)'),
),
],
),
const SizedBox(height: 25),
if (_selectedImageBytes != null) ...[
const Text('Source Preview:', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 10),
Container(
height: 180,
width: 320,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(7),
child: Image.memory(_selectedImageBytes!, fit: BoxFit.contain),
),
),
const SizedBox(height: 25),
],
const Text(
'Parsed Pipeline Outputs:',
style: TextStyle(decoration: TextDecoration.underline),
),
const SizedBox(height: 10),
if (_extractedData != null)
Card(
color: Colors.grey[50],
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SelectableText(
_extractedData!.entries.map((e) => '${e.key}: ${e.value}').join('\n'),
style: const TextStyle(fontFamily: 'monospace', fontSize: 13),
),
),
),
],
),
),
),
);
}
}