11import 'dart:ui' ;
2-
32import 'package:flutter/material.dart' ;
4- import 'package:camera/camera.dart' ;
53import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart' ;
64import 'package:image_picker/image_picker.dart' ;
75
8- class CameraScreen extends StatefulWidget {
9- const CameraScreen ({super .key, required this .cameras});
10- final List <CameraDescription > cameras;
6+ class TextRecognitionScreen extends StatefulWidget {
7+ const TextRecognitionScreen ({super .key});
118
129 @override
13- _CameraScreenState createState () => _CameraScreenState ();
10+ _TextRecognitionScreenState createState () => _TextRecognitionScreenState ();
1411}
1512
16- class _CameraScreenState extends State <CameraScreen > {
17- late CameraController _controller;
13+ class _TextRecognitionScreenState extends State <TextRecognitionScreen > {
1814 late TextRecognizer _textRecognizer;
19- String _recognizedText = 'Tekan capture untuk scan teks' ;
15+ String _recognizedText = 'Pilih gambar untuk memulai scan teks' ;
2016 bool _isProcessing = false ;
2117
2218 TextRecognitionScript _currentScript = TextRecognitionScript .latin;
2319
2420 @override
2521 void initState () {
2622 super .initState ();
27- _initializeCamera ();
28- // _textRecognizer = TextRecognizer(script: TextRecognitionScript.latin);
2923 _textRecognizer = TextRecognizer (script: _currentScript);
3024 }
3125
3226 // Method untuk ganti bahasa
3327 void _changeLanguage (TextRecognitionScript newScript) {
3428 _textRecognizer.close ();
3529
36- // buat recognizer baru dengan bahasa yang ingin dipilih
3730 setState (() {
3831 _currentScript = newScript;
3932 _textRecognizer = TextRecognizer (script: _currentScript);
4033 });
4134 }
4235
43- void _initializeCamera () async {
44- _controller = CameraController (
45- widget.cameras[0 ],
46- ResolutionPreset .max
47- );
48-
49- await _controller.initialize ();
50- if (mounted) setState (() {});
51- }
52-
53- void _captureAndRecognize () async {
54- if (_isProcessing) return ;
55-
56- setState (() {
57- _isProcessing = true ;
58- _recognizedText = 'Memproses gambar...' ;
59- });
60-
61- try {
62- final XFile picture = await _controller.takePicture ();
63- await _processImageForTextRecognition (picture.path);
64- final inputImage = InputImage .fromFilePath (picture.path);
65- final RecognizedText recognizedText = await _textRecognizer.processImage (inputImage);
66-
67- String fullText = '' ;
68- for (TextBlock block in recognizedText.blocks) {
69- for (TextLine line in block.lines) {
70- fullText += '${line .text }\n ' ;
71- }
72- }
73-
74- setState (() {
75- _recognizedText = fullText.isEmpty ? 'Tidak ada teks terdeteksi' : fullText;
76- });
77- } catch (e) {
78- setState (() {
79- _recognizedText = 'Error: $e ' ;
80- });
81- } finally {
82- setState (() => _isProcessing = false );
83- }
84- }
85-
86- void _showLanguageSelection () { // Dialog Language Selection
36+ void _showLanguageSelection () {
8737 showDialog (
8838 barrierDismissible: false ,
8939 context: context,
@@ -139,7 +89,7 @@ class _CameraScreenState extends State<CameraScreen> {
13989 );
14090 }
14191
142- // memilih sumber gambar
92+ // memilih sumber gambar (galeri atau camera)
14393 void _showImageSourceSelection () {
14494 showModalBottomSheet (
14595 context: context,
@@ -177,10 +127,9 @@ class _CameraScreenState extends State<CameraScreen> {
177127 final XFile ? image = await picker.pickImage (source: ImageSource .gallery);
178128
179129 if (image != null ) {
180- _processImageForTextRecognition (image.path);
130+ await _processImageForTextRecognition (image.path);
181131 }
182132 } catch (e) {
183- print ('Error picking image from gallery: $e ' );
184133 setState (() {
185134 _recognizedText = 'Error: Gagal memilih gambar dari galeri' ;
186135 });
@@ -194,17 +143,16 @@ class _CameraScreenState extends State<CameraScreen> {
194143 final XFile ? image = await picker.pickImage (source: ImageSource .camera);
195144
196145 if (image != null ) {
197- _processImageForTextRecognition (image.path);
146+ await _processImageForTextRecognition (image.path);
198147 }
199148 } catch (e) {
200- print ('Error taking picture: $e ' );
201149 setState (() {
202150 _recognizedText = 'Error: Gagal mengambil gambar' ;
203151 });
204152 }
205153 }
206154
207- // memproses gambar dan print ke terminal
155+ // memproses gambar
208156 Future <void > _processImageForTextRecognition (String imagePath) async {
209157 if (_isProcessing) return ;
210158
@@ -215,49 +163,23 @@ class _CameraScreenState extends State<CameraScreen> {
215163
216164 try {
217165 final inputImage = InputImage .fromFilePath (imagePath);
218- final textRecognizer = TextRecognizer (
219- script: TextRecognitionScript .latin,
220- );
221- final RecognizedText recognizedText = await textRecognizer.processImage (
222- inputImage,
223- );
224-
166+ final RecognizedText recognizedText = await _textRecognizer.processImage (inputImage);
225167 String fullText = '' ;
226168
227- // Loop melalui semua blok teks yang terdeteksi
169+ // gunakan variable recognizedText untuk ekstraksi teks
228170 for (TextBlock block in recognizedText.blocks) {
229- print ('=== BLOCK TEXT ===' );
230- print (block.text);
231- print ('==================' );
232-
233171 for (TextLine line in block.lines) {
234172 fullText += '${line .text }\n ' ;
235-
236- // Print setiap line ke terminal
237- print ('Line: ${line .text }' );
238-
239- // Jika ingin detail lebih lanjut, bisa print setiap element
240- for (TextElement element in line.elements) {
241- print (' Element: ${element .text }' );
242- }
243173 }
244174 }
245175
246- // Print keseluruhan teks ke terminal
247- print ('=== HASIL EKSTRAKSI TEKS LENGKAP ===' );
248- print (fullText);
249- print ('=====================================' );
250-
251176 setState (() {
252177 _recognizedText = fullText.isEmpty
253178 ? 'Tidak ada teks terdeteksi'
254179 : fullText;
255180 });
256181
257- // Tutup text recognizer untuk menghindari memory leak
258- textRecognizer.close ();
259182 } catch (e) {
260- print ('Error processing image: $e ' );
261183 setState (() {
262184 _recognizedText = 'Error: Gagal memproses gambar' ;
263185 });
@@ -268,19 +190,14 @@ class _CameraScreenState extends State<CameraScreen> {
268190
269191 @override
270192 Widget build (BuildContext context) {
271- if (! _controller.value.isInitialized) {
272- return const Scaffold (
273- body: Center (child: CircularProgressIndicator ()),
274- );
275- }
276-
277193 return Scaffold (
278194 appBar: AppBar (
195+ backgroundColor: Colors .blue,
279196 actions: [
280- DropdownButton <TextRecognitionScript >( //* Dropdown button untuk pilihan bahasa
197+ DropdownButton <TextRecognitionScript >(
281198 value: _currentScript,
282199 icon: const Icon (Icons .language, color: Colors .black),
283- dropdownColor: Colors .blue ,
200+ dropdownColor: Colors .white ,
284201 onChanged: (TextRecognitionScript ? newScript) {
285202 if (newScript != null ) {
286203 _changeLanguage (newScript);
@@ -309,19 +226,35 @@ class _CameraScreenState extends State<CameraScreen> {
309226 ),
310227 ],
311228 ),
312- const SizedBox (width: 20 ),
229+ const SizedBox (width: 16 ),
313230 ],
314231 ),
315232 body: Column (
316233 children: [
317- Expanded (
318- flex: 3 ,
319- child: CameraPreview (_controller),
234+ // Header dengan instruksi
235+ Container (
236+ padding: const EdgeInsets .all (16 ),
237+ child: const Column (
238+ children: [
239+ SizedBox (height: 8 ),
240+ Text (
241+ 'Pilih gambar dari galeri atau ambil foto baru untuk mengekstrak teks' ,
242+ textAlign: TextAlign .center,
243+ style: TextStyle (fontSize: 14 , color: Colors .grey),
244+ ),
245+ ],
246+ ),
320247 ),
248+
249+ // Area hasil teks
321250 Expanded (
322- flex: 1 ,
323251 child: Container (
252+ margin: const EdgeInsets .all (16 ),
324253 padding: const EdgeInsets .all (16 ),
254+ decoration: BoxDecoration (
255+ border: Border .all (color: Colors .grey.shade400),
256+ borderRadius: BorderRadius .circular (12 ),
257+ ),
325258 child: SingleChildScrollView (
326259 child: Text (
327260 _recognizedText,
@@ -333,29 +266,23 @@ class _CameraScreenState extends State<CameraScreen> {
333266 ],
334267 ),
335268 floatingActionButton: Padding (
336- padding: const EdgeInsets .only (left : 30 ),
269+ padding: const EdgeInsets .only (bottom : 20 ),
337270 child: Row (
338271 mainAxisAlignment: MainAxisAlignment .center,
339272 children: [
340- FloatingActionButton (
341- onPressed: _captureAndRecognize,
342- backgroundColor: _isProcessing ? Colors .grey: Colors .blue,
343- elevation: 20 , // biar ada shadow effect pada icon
344- child: _isProcessing
345- ? const CircularProgressIndicator (color: Colors .white)
346- : const Icon (Icons .camera)
347- ),
348273 const SizedBox (width: 20 ),
349274 FloatingActionButton (
350275 onPressed: _showImageSourceSelection,
351- backgroundColor: Colors .green,
352- child: const Icon (Icons .photo_library)
276+ backgroundColor: Colors .blue,
277+ elevation: 10 ,
278+ child: const Icon (Icons .add_photo_alternate, color: Colors .black)
353279 ),
354280 const SizedBox (width: 20 ),
355281 FloatingActionButton (
356282 onPressed: _showLanguageSelection,
357283 backgroundColor: Colors .orange,
358- child: const Icon (Icons .language)
284+ elevation: 10 ,
285+ child: const Icon (Icons .language, color: Colors .black)
359286 ),
360287 ],
361288 ),
@@ -365,7 +292,6 @@ class _CameraScreenState extends State<CameraScreen> {
365292
366293 @override
367294 void dispose () {
368- _controller.dispose ();
369295 _textRecognizer.close ();
370296 super .dispose ();
371297 }
0 commit comments