import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:praresi/presentation/controllers/resi_controller.dart'; class OCRView extends StatefulWidget { const OCRView({super.key}); @override State createState() => _OCRViewState(); } class _OCRViewState extends State { File? _imageFile; final picker = ImagePicker(); final TextEditingController _textController = TextEditingController(); bool _isProcessing = false; bool _isSaving = false; final ResiController resiController = Get.put(ResiController()); final FirebaseAuth _auth = FirebaseAuth.instance; /// Ambil gambar dan jalankan OCR Future _pickImage(ImageSource source) async { final pickedFile = await picker.pickImage(source: source, imageQuality: 80); if (pickedFile != null) { File? cropped = await _cropImage(pickedFile.path); if (cropped != null) { setState(() { _imageFile = cropped; }); await _performOCR(cropped); } } } @override void initState() { super.initState(); _textController.addListener(() { setState(() {}); }); } /// Crop gambar Future _cropImage(String imagePath) async { final croppedFile = await ImageCropper().cropImage( sourcePath: imagePath, aspectRatioPresets: [ CropAspectRatioPreset.original, CropAspectRatioPreset.square, CropAspectRatioPreset.ratio4x3, CropAspectRatioPreset.ratio16x9, ], uiSettings: [ AndroidUiSettings( toolbarTitle: 'Crop Gambar', toolbarColor: const Color(0xFF1976D2), toolbarWidgetColor: Colors.white, activeControlsWidgetColor: const Color(0xFF1976D2), initAspectRatio: CropAspectRatioPreset.original, lockAspectRatio: false, // 🔓 supaya bisa geser & ubah ukuran bebas ), IOSUiSettings( title: 'Crop Gambar', aspectRatioLockEnabled: false, // 🔓 untuk iPhone/iPad ), ], ); return croppedFile != null ? File(croppedFile.path) : null; } /// Jalankan OCR pakai Google ML Kit Future _performOCR(File imageFile) async { setState(() { _isProcessing = true; }); final inputImage = InputImage.fromFile(imageFile); final textRecognizer = TextRecognizer(); final recognizedText = await textRecognizer.processImage(inputImage); await textRecognizer.close(); setState(() { _textController.text = recognizedText.text; _isProcessing = false; }); Get.snackbar( 'Selesai', 'Teks berhasil dikenali!', backgroundColor: Colors.green, colorText: Colors.white, ); } /// Simpan hasil OCR ke Firestore Future _saveResi() async { /// ✅ cegah double klik if (_isSaving) return; final user = _auth.currentUser; if (user == null) { Get.snackbar( 'Gagal', 'User belum login!', backgroundColor: Colors.red, colorText: Colors.white, ); return; } if (_textController.text.trim().isEmpty) { Get.snackbar( 'Peringatan', 'Tidak ada teks untuk disimpan!', backgroundColor: Colors.orange, colorText: Colors.white, ); return; } setState(() { _isSaving = true; }); try { final storeId = user.uid; await resiController.saveResi( _textController.text.trim(), storeId, ); } finally { /// ✅ aktif kembali setelah selesai setState(() { _isSaving = false; }); } } @override void dispose() { _textController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; return Scaffold( appBar: AppBar( title: const Text("Pindai Pemesanan"), centerTitle: true, backgroundColor: const Color(0xFF1976D2), foregroundColor: Colors.white, ), body: Container( width: size.width, height: size.height, decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF1976D2), Color(0xFFE3F2FD)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: SafeArea( child: Column( children: [ const SizedBox(height: 20), // Gambar hasil ambil/crop Expanded( flex: 4, child: Container( width: double.infinity, margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 5), ) ], ), child: _imageFile == null ? Column( mainAxisAlignment: MainAxisAlignment.center, children: const [ Icon(Icons.camera_alt, size: 50, color: Colors.grey), SizedBox(height: 12), Text( "Belum ada gambar", style: TextStyle( fontSize: 16, color: Colors.grey), ), ], ) : ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.file(_imageFile!, fit: BoxFit.cover), ), ), ), const SizedBox(height: 20), // Tombol ambil/pilih gambar Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _pickImage(ImageSource.camera), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF1976D2), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), icon: const Icon(Icons.camera, color: Colors.white), label: const Text("Kamera", style: TextStyle(color: Colors.white, fontSize: 16)), ), ), const SizedBox(width: 10), Expanded( child: OutlinedButton.icon( onPressed: () => _pickImage(ImageSource.gallery), style: OutlinedButton.styleFrom( side: const BorderSide(color: Color(0xFF1976D2), width: 2), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), icon: const Icon(Icons.photo_library, color: Color(0xFF1976D2)), label: const Text("Galeri", style: TextStyle( color: Color(0xFF1976D2), fontSize: 16)), ), ), ], ), ), const SizedBox(height: 20), // Area hasil OCR Expanded( flex: 3, child: Stack( children: [ Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 5), ) ], ), child: _isProcessing ? const Center( child: CircularProgressIndicator( color: Color(0xFF1976D2), ), ) : TextField( controller: _textController, maxLines: null, decoration: const InputDecoration( border: InputBorder.none, hintText: "Hasil OCR akan muncul di sini...", ), ), ), // =============================== // ICON HAPUS // =============================== if (_textController.text.isNotEmpty) Positioned( top: 8, right: 24, child: GestureDetector( onTap: () { setState(() { _textController.clear(); }); }, child: Container( decoration: BoxDecoration( color: Colors.grey.shade200, shape: BoxShape.circle, ), padding: const EdgeInsets.all(6), child: const Icon( Icons.close, size: 18, color: Colors.black54, ), ), ), ), ], ), ), const SizedBox(height: 10), // Tombol Simpan ke Firestore Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SizedBox( width: double.infinity, height: 50, child: ElevatedButton.icon( /// ✅ disable saat saving onPressed: _isSaving ? null : _saveResi, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), /// ✅ loading icon icon: _isSaving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Icon(Icons.save, color: Colors.white), label: Text( _isSaving ? "Menyimpan..." : "Simpan", style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ), const SizedBox(height: 20), ], ), ), ), ); } }