diff --git a/lib/features/auth/presentation/screen/collector/identity_validation_screen.dart b/lib/features/auth/presentation/screen/collector/identity_validation_screen.dart index 72cfb11..20344c5 100644 --- a/lib/features/auth/presentation/screen/collector/identity_validation_screen.dart +++ b/lib/features/auth/presentation/screen/collector/identity_validation_screen.dart @@ -2,6 +2,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:image/image.dart' as img; +import 'package:path_provider/path_provider.dart'; import 'package:rijig_mobile/core/utils/guide.dart'; import 'package:rijig_mobile/widget/buttoncard.dart'; @@ -14,51 +16,110 @@ class UploadKtpScreen extends StatefulWidget { class _UploadKtpScreenState extends State { File? _ktpImage; - String nik = ''; - String nama = ''; - String alamat = ''; bool isLoading = false; + final Map controllers = { + "NIK": TextEditingController(), + "Nama": TextEditingController(), + "Tempat/Tgl Lahir": TextEditingController(), + "Alamat": TextEditingController(), + "RT/RW": TextEditingController(), + "Kel/Desa": TextEditingController(), + "Kecamatan": TextEditingController(), + "Agama": TextEditingController(), + "Status Perkawinan": TextEditingController(), + "Pekerjaan": TextEditingController(), + "Kewarganegaraan": TextEditingController(), + "Berlaku Hingga": TextEditingController(), + }; + + final Map> labelSynonyms = { + "NIK": ["nik"], + "Nama": ["nama", "nama lengkap"], + "Tempat/Tgl Lahir": ["tempat/tgl lahir", "tempat lahir", "tgl lahir"], + "Alamat": ["alamat"], + "RT/RW": ["rt/rw", "rtrw", "rt rw"], + "Kel/Desa": ["kelurahan", "desa", "kel/desa"], + "Kecamatan": ["kecamatan"], + "Agama": ["agama"], + "Status Perkawinan": ["status", "status perkawinan", "perkawinan"], + "Pekerjaan": ["pekerjaan"], + "Kewarganegaraan": ["kewarganegaraan", "warga negara"], + "Berlaku Hingga": ["berlaku", "berlaku hingga"], + }; + Future _pickImage() async { - final pickedFile = await ImagePicker().pickImage(source: ImageSource.camera); + final pickedFile = await ImagePicker().pickImage( + source: ImageSource.camera, + ); if (pickedFile != null) { setState(() { isLoading = true; _ktpImage = File(pickedFile.path); }); - await _processImage(File(pickedFile.path)); + await _processImageWithEnhancements(File(pickedFile.path)); setState(() => isLoading = false); } } - //dari kode ini - Future _processImage(File imageFile) async { - final inputImage = InputImage.fromFile(imageFile); + Future _processImageWithEnhancements(File imageFile) async { + final rawBytes = await imageFile.readAsBytes(); + final originalImage = img.decodeImage(rawBytes); + if (originalImage == null) return; + + // Enhance image: grayscale + auto rotate + thresholding + var processedImage = img.grayscale(originalImage); + if (processedImage.width > processedImage.height) { + processedImage = img.copyRotate(processedImage, angle: -90); + } + // processedImage = img.threshold(processedImage, threshold: 128); + + final tempDir = await getTemporaryDirectory(); + final enhancedPath = '${tempDir.path}/enhanced_ktp.jpg'; + final enhancedFile = File(enhancedPath) + ..writeAsBytesSync(img.encodeJpg(processedImage)); + + final inputImage = InputImage.fromFile(enhancedFile); final textRecognizer = TextRecognizer(script: TextRecognitionScript.latin); - final RecognizedText recognizedText = await textRecognizer.processImage(inputImage); + final RecognizedText recognizedText = await textRecognizer.processImage( + inputImage, + ); await textRecognizer.close(); - final text = recognizedText.text; + final lines = recognizedText.text.split('\n'); + debugPrint("[OCR Result]\n${recognizedText.text}"); + setState(() { - nik = _extractValue("NIK", text); - nama = _extractValue("Nama", text); - alamat = _extractValue("Alamat", text); + for (var key in controllers.keys) { + final value = _extractMultilineValue(key, lines); + controllers[key]?.text = value; + } }); } - // terdapat error 'textRecognizer' is deprecated and shouldn't be used. Use [google_mlkit_text_recognition] plugin instead of [google_ml_kit]. -// Try replacing the use of the deprecated member with the replacement.dartdeprecated_member_use -// (deprecated) TextRecognizer textRecognizer({dynamic script = TextRecognitionScript.latin}) -// Type: TextRecognizer Function({dynamic script}) + String _extractMultilineValue(String key, List lines) { + final List keywords = labelSynonyms[key] ?? [key]; + for (int i = 0; i < lines.length; i++) { + final line = lines[i].toLowerCase(); + for (final kw in keywords) { + if (line.contains(kw.toLowerCase())) { + if (lines[i].contains(":")) { + return lines[i].split(":").last.trim(); + } else if (i + 1 < lines.length) { + return lines[i + 1].trim(); + } + } + } + } + return ''; + } -// package:google_ml_kit/src/vision.dart - -// Return an instance of [TextRecognizer]. - - String _extractValue(String key, String fullText) { - final regex = RegExp('${key}s*[:s]?s*(.*)', caseSensitive: false); - final match = regex.firstMatch(fullText); - return match?.group(1)?.split("\n").first.trim() ?? ''; + @override + void dispose() { + for (final controller in controllers.values) { + controller.dispose(); + } + super.dispose(); } @override @@ -79,40 +140,33 @@ class _UploadKtpScreenState extends State { _ktpImage != null ? Image.file(_ktpImage!, height: 200) : Container( - height: 200, - color: Colors.grey.shade300, - child: const Center(child: Text("Belum ada gambar KTP")), - ), + height: 200, + color: Colors.grey.shade300, + child: const Center(child: Text("Belum ada gambar KTP")), + ), const SizedBox(height: 20), isLoading ? const CircularProgressIndicator() : CardButtonOne( - textButton: "Upload Foto KTP", - fontSized: 16, - colorText: whiteColor, - color: primaryColor, - borderRadius: 10, - horizontal: double.infinity, - vertical: 50, - onTap: _pickImage, - usingRow: false, - ), + textButton: "Upload Foto KTP", + fontSized: 16, + colorText: whiteColor, + color: primaryColor, + borderRadius: 10, + horizontal: double.infinity, + vertical: 50, + onTap: _pickImage, + usingRow: false, + ), const SizedBox(height: 30), - TextField( - decoration: const InputDecoration(labelText: 'NIK'), - controller: TextEditingController(text: nik), - onChanged: (val) => nik = val, - ), - TextField( - decoration: const InputDecoration(labelText: 'Nama'), - controller: TextEditingController(text: nama), - onChanged: (val) => nama = val, - ), - TextField( - decoration: const InputDecoration(labelText: 'Alamat'), - controller: TextEditingController(text: alamat), - onChanged: (val) => alamat = val, - ), + for (var key in controllers.keys) + Padding( + padding: const EdgeInsets.only(bottom: 12), + child: TextField( + decoration: InputDecoration(labelText: key), + controller: controllers[key], + ), + ), ], ), ), diff --git a/pubspec.lock b/pubspec.lock index 3d458f2..6938991 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -425,7 +425,7 @@ packages: source: hosted version: "1.0.0" image: - dependency: transitive + dependency: "direct main" description: name: image sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" @@ -641,7 +641,7 @@ packages: source: hosted version: "1.1.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" diff --git a/pubspec.yaml b/pubspec.yaml index a0319bd..04a382f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,10 +31,12 @@ dependencies: http: ^1.3.0 http_parser: ^4.1.2 iconsax_flutter: ^1.0.0 + image: ^4.5.4 image_picker: ^1.1.2 intl: ^0.20.2 jwt_decoder: ^2.0.1 localstorage: ^6.0.0 + path_provider: ^2.1.5 pin_code_fields: ^8.0.1 provider: ^6.1.4 shared_preferences: ^2.3.3