import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:camera/camera.dart'; import 'dart:io'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:intl/intl.dart'; import 'package:image_cropper/image_cropper.dart'; import 'result_page.dart'; class CameraPage extends StatefulWidget { const CameraPage({super.key}); @override _CameraPageState createState() => _CameraPageState(); } class _CameraPageState extends State { CameraController? _controller; late List _cameras; bool _isCameraInitialized = false; bool _isUploading = false; @override void initState() { super.initState(); _initializeCamera(); } Future _initializeCamera() async { _cameras = await availableCameras(); _controller = CameraController(_cameras[0], ResolutionPreset.medium); await _controller!.initialize(); if (!mounted) return; setState(() { _isCameraInitialized = true; }); } Future _captureImage() async { if (!_controller!.value.isInitialized) return; final XFile imageFile = await _controller!.takePicture(); File? croppedImage = await _cropImage(File(imageFile.path)); if (croppedImage != null) { final String date = DateFormat('dd-MM-yyyy HH:mm:ss').format(DateTime.now()); _uploadImage(croppedImage, date); } else { print("❌ Cropping dibatalkan."); } } Future _cropImage(File imageFile) async { try { final CroppedFile? croppedFile = await ImageCropper().cropImage( sourcePath: imageFile.path, compressFormat: ImageCompressFormat.jpg, compressQuality: 80, aspectRatioPresets: [ CropAspectRatioPreset.square, CropAspectRatioPreset.ratio3x2, CropAspectRatioPreset.original, CropAspectRatioPreset.ratio4x3, CropAspectRatioPreset.ratio16x9 ], uiSettings: [ AndroidUiSettings( toolbarTitle: 'Crop Gambar', toolbarColor: const Color(0xFF891A2D), toolbarWidgetColor: Colors.white, initAspectRatio: CropAspectRatioPreset.original, lockAspectRatio: false, ), IOSUiSettings(title: 'Crop Gambar'), ], ); return croppedFile != null ? File(croppedFile.path) : null; } catch (e) { print("❌ Error saat cropping gambar: $e"); return null; } } Future _uploadImage(File image, String date) async { setState(() { _isUploading = true; }); var request = http.MultipartRequest( 'POST', Uri.parse('http://172.20.10.10:5000/predict')); request.files.add(await http.MultipartFile.fromPath('file', image.path)); var response = await request.send(); if (response.statusCode == 200) { var responseData = await response.stream.bytesToString(); var result = json.decode(responseData); String prediction = result['prediction']; setState(() { _isUploading = false; }); Navigator.push( context, MaterialPageRoute( builder: (context) => ResultPage( image: image, date: date, prediction: prediction, ), ), ); } else { setState(() { _isUploading = false; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Terjadi kesalahan. Coba lagi.")), ); } } @override void dispose() { _controller?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, flexibleSpace: Container( decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF891A2D), Color(0xFF891A2D)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), ), title: Text( "Ambil Gambar", style: GoogleFonts.poppins( color: Colors.white, // fontWeight: FontWeight.w600, fontSize: 20, ), ), centerTitle: true, iconTheme: const IconThemeData(color: Colors.white), ), body: Stack( children: [ Column( children: [ Expanded( child: Stack( children: [ // Tampilan Kamera _isCameraInitialized ? CameraPreview(_controller!) : const Center(child: CircularProgressIndicator()), // Bingkai Panduan untuk Membantu Pengguna // Center( // child: Container( // width: 300, // height: 300, // decoration: BoxDecoration( // border: Border.all( // color: Colors.white.withOpacity(0.7), // width: 2, // style: BorderStyle.solid, // ), // borderRadius: BorderRadius.circular(15), // ), // child: Center( // child: Text( // "Letakkan stroberi di sini", // style: GoogleFonts.poppins( // color: Colors.white.withOpacity(0.8), // fontSize: 14, // ), // ), // ), // ), // ), // Petunjuk Penggunaan dan Tombol Align( alignment: Alignment.bottomCenter, child: Container( width: double.infinity, decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.vertical( top: Radius.circular(20), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 10, spreadRadius: 5, ), ], ), padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Petunjuk Penggunaan dalam Kartu Card( elevation: 3, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), color: Colors.grey[100], child: Padding( padding: const EdgeInsets.all(15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.info_outline, color: Color(0xFF891A2D), size: 24, ), const SizedBox(width: 8), Text( "Petunjuk Penggunaan", style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, color: const Color(0xFF891A2D), ), ), ], ), const SizedBox(height: 10), Text( "Arahkan kamera ke buah stroberi tanpa tangkai dalam pencahayaan yang baik untuk mendapatkan hasil terbaik", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 14, color: Colors.black87, height: 1.6, ), ), ], ), ), ), const SizedBox(height: 20), // Tombol Ambil Gambar dengan Animasi TweenAnimationBuilder( tween: Tween(begin: 1.0, end: 1.0), duration: const Duration(milliseconds: 200), builder: (context, scale, child) { return Transform.scale( scale: scale, child: child, ); }, child: ElevatedButton.icon( onPressed: _isUploading ? null : _captureImage, icon: const Icon( Icons.camera_alt, size: 28, color: Colors.white, ), label: Text( "Ambil Gambar", style: GoogleFonts.poppins( fontSize: 16, color: Colors.white, fontWeight: FontWeight.w600, ), ), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF891A2D), padding: const EdgeInsets.symmetric( vertical: 15, horizontal: 30), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), elevation: 5, ), ), ), ], ), ), ), ], ), ), ], ), // Indikator Loading if (_isUploading) Container( color: Colors.black.withOpacity(0.6), child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator( color: Color(0xFF891A2D), strokeWidth: 5, ), const SizedBox(height: 15), Text( "Mengunggah Gambar...", style: GoogleFonts.poppins( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500, ), ), ], ), ), ), ], ), ); } }