import 'dart:io'; import 'dart:ui'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:provider/provider.dart'; import '../Provider/driver_provider.dart'; import '../Provider/language_provider.dart'; import '../Provider/utils_singleton.dart'; import '../config/AppSnackbar.dart'; import '../config/constants/app_constant.dart'; import '../config/language/language_enum.dart'; import '../utils/colors.dart'; import '../widgets/DottedBorderContainer.dart'; import '../widgets/buildDocumentSection.dart'; import '../widgets/carDetails.dart'; import '../widgets/custom_button.dart'; import '../widgets/custom_text.dart'; class VehiculeScreen extends StatefulWidget { const VehiculeScreen({super.key}); @override State createState() => _VehiculeScreenState(); } class _VehiculeScreenState extends State { // Controllers final TextEditingController _yearController = TextEditingController(); final TextEditingController _plateController = TextEditingController(); // Image Picker final ImagePicker _picker = ImagePicker(); // Image variables - can be managed with provider File? _carteGriseImage; File? _insuranceImage; List _carImages = []; // Changed to list // Image URLs from server String? _carteGriseUrl; String? _insuranceUrl; List _carImageUrls = []; // Changed to list @override void initState() { super.initState(); _initializeData(); } void _initializeData() { _yearController.text = utils.user?.driver!.vehicle?.vehicleYear?.toString() ?? ''; _plateController.text = utils.user?.driver!.vehicle?.plateNumber ?? ''; _carteGriseUrl = utils.user?.driver!.vehicle?.carLicence?.url; _insuranceUrl = utils.user?.driver!.vehicle?.insurance?.url; // Load list of car images if (utils.user?.driver!.vehicle?.images != null && utils.user!.driver!.vehicle!.images.isNotEmpty) { _carImageUrls = utils.user!.driver!.vehicle!.images .map((img) => img.url ?? '') .where((url) => url.isNotEmpty) .toList(); } } @override void dispose() { _yearController.dispose(); _plateController.dispose(); super.dispose(); } // Pick image from gallery or camera Future _pickImage(String type) async { showModalBottomSheet( context: context, builder: (BuildContext context) { return SafeArea( child: Wrap( children: [ ListTile( leading: const Icon(Icons.photo_library), title: const Text('Choose from Gallery'), onTap: () async { Navigator.pop(context); final XFile? image = await _picker.pickImage( source: ImageSource.gallery, imageQuality: 85, ); if (image != null) { _updateImage(type, File(image.path)); } }, ), ListTile( leading: const Icon(Icons.camera_alt), title: const Text('Take a Photo'), onTap: () async { Navigator.pop(context); final XFile? image = await _picker.pickImage( source: ImageSource.camera, imageQuality: 85, ); if (image != null) { _updateImage(type, File(image.path)); } }, ), ], ), ); }, ); } // Pick multiple car images Future _pickMultipleCarImages() async { showModalBottomSheet( context: context, builder: (BuildContext context) { return SafeArea( child: Wrap( children: [ ListTile( leading: const Icon(Icons.photo_library), title: const Text('Choose Multiple from Gallery'), onTap: () async { Navigator.pop(context); final List images = await _picker.pickMultiImage( imageQuality: 85, ); if (images.isNotEmpty) { setState(() { _carImages.addAll(images.map((img) => File(img.path))); }); // TODO: Upload images to server } }, ), ListTile( leading: const Icon(Icons.camera_alt), title: const Text('Take a Photo'), onTap: () async { Navigator.pop(context); final XFile? image = await _picker.pickImage( source: ImageSource.camera, imageQuality: 85, ); if (image != null) { setState(() { _carImages.add(File(image.path)); }); // TODO: Upload image to server } }, ), ], ), ); }, ); } void _updateImage(String type, File image) { setState(() { switch (type) { case 'carte_grise': _carteGriseImage = image; break; case 'insurance': _insuranceImage = image; break; } }); // TODO: Upload image to server using provider // Example: Provider.of(context, listen: false).uploadImage(type, image); } void _removeCarImage(int index, bool isLocal) { setState(() { if (isLocal) { _carImages.removeAt(index); } else { _carImageUrls.removeAt(index); // TODO: Delete image from server } }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, body: SafeArea( child: Column( children: [ // Header Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), child: Row( children: [ Container( decoration: BoxDecoration( shape: BoxShape.circle, color: Theme.of(context).brightness == Brightness.dark ? Colors.grey.shade800 : Colors.grey.shade200, ), child: IconButton( icon: Icon( Icons.arrow_back, color: Theme.of(context).brightness == Brightness.dark ? AppColors.white : AppColors.black, size: 20, ), onPressed: () => Navigator.pop(context), ), ), const Expanded( child: Center( child: Text( 'Vehicle', style: TextStyle( color: Colors.black, fontSize: 17, fontFamily: 'Inter', fontWeight: FontWeight.w600, ), ), ), ), const SizedBox(width: 24), ], ), ), // Content Expanded( child: utils.user?.driver!.vehicle != null ? SingleChildScrollView( padding: const EdgeInsets.fromLTRB(16, 8, 16, 24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Vehicle Brand - READ ONLY CarDetailsWidget( label: 'Vehicle Brand', value: utils.user?.driver!.vehicle?.vehicleModel ?? '', isEditable: false, ), const SizedBox(height: 16), // Model - READ ONLY CarDetailsWidget( label: 'Model', value: utils.user?.driver!.vehicle?.vehicleType.name ?? '', isEditable: false, ), const SizedBox(height: 16), // Vehicle Year - EDITABLE CarDetailsWidget( label: 'Vehicle Year', controller: _yearController, isEditable: true, ), const SizedBox(height: 16), // Plate Number - EDITABLE CarDetailsWidget( label: 'Plate Number', controller: _plateController, hint: "exp : 20TN2013", isEditable: true, ), const SizedBox(height: 16), // Carte Grise DocumentSectionWidget( label: 'Carte Grise', localImage: _carteGriseImage, imageUrl: _carteGriseUrl, onTap: () => _pickImage('carte_grise'), ), const SizedBox(height: 16), // Insurance DocumentSectionWidget( label: 'Insurance', localImage: _insuranceImage, imageUrl: _insuranceUrl, onTap: () => _pickImage('insurance'), ), const SizedBox(height: 16), // Car Images - Multiple _buildCarImagesSection(), const SizedBox(height: 16), CustomButton( borderRadius: 10, height: 50, isloading: false, onClick: () async { final driverProvider = Provider.of(context, listen: false); // Validate that at least something has changed bool hasChanges = _carteGriseImage != null || _insuranceImage != null || _carImages.isNotEmpty || _yearController.text != (utils.user?.driver!.vehicle?.vehicleYear?.toString() ?? '') || _plateController.text != (utils.user?.driver!.vehicle?.plateNumber ?? ''); if (!hasChanges) { AppSnackbar.showInfo("Info", "No changes to save"); return; } // Parse vehicle year int? vehicleYear; if (_yearController.text.isNotEmpty) { vehicleYear = int.tryParse(_yearController.text); if (vehicleYear == null) { AppSnackbar.showError("Error", "Please enter a valid year"); return; } } // Get plate number String? plateNumber = _plateController.text.isNotEmpty ? _plateController.text : null; // Call the update method bool success = await driverProvider.updateVehicleImages( carteGrise: _carteGriseImage, insurance: _insuranceImage, carImages: _carImages.isNotEmpty ? _carImages : null, vehicleYear: vehicleYear, plateNumber: plateNumber, ); if (success) { // Navigate back or show success message Navigator.pop(context); } }, title: 'Update', width: MediaQuery.of(context).size.width, backgroundColor: Theme.of(context).brightness == Brightness.dark ? AppColors.darkPrimaryColor : AppColors.primaryColor, ), ], ), ) : const Center(child: Text('No vehicle data')), ), ], ), ), ); } Widget _buildCarImagesSection() { final totalImages = _carImages.length + _carImageUrls.length; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Car Images', style: TextStyle( color: Color(0xFFAAAAAA), fontSize: 13, fontFamily: 'Inter', fontWeight: FontWeight.w500, letterSpacing: -0.08, ), ), const SizedBox(height: 10), // Grid of images if (totalImages > 0) GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, childAspectRatio: 1, ), itemCount: totalImages + 1, // +1 for add button itemBuilder: (context, index) { // Add button if (index == totalImages) { return GestureDetector( onTap: _pickMultipleCarImages, child: DottedBorderContainer( child: Container( decoration: BoxDecoration( color: const Color(0xFFFAFAFA), borderRadius: BorderRadius.circular(10), ), child: Center( child: Image.asset( 'assets/images/icon_button.png', width: 70, height: 70, ), ), ), ), ); } // Display local images first if (index < _carImages.length) { return _buildImageItem( image: _carImages[index], index: index, isLocal: true, ); } // Display server images final urlIndex = index - _carImages.length; return _buildImageItem( imageUrl: _carImageUrls[urlIndex], index: urlIndex, isLocal: false, ); }, ) else // Empty state - show add button GestureDetector( onTap: _pickMultipleCarImages, child: DottedBorderContainer( child: Container( width: double.infinity, height: 90, decoration: BoxDecoration( color: const Color(0xFFFAFAFA), borderRadius: BorderRadius.circular(10), ), child: Center( child: Image.asset( 'assets/images/icon_button.png', width: 100, height: 100, ), ), ), ), ), ], ); } Widget _buildImageItem({ File? image, String? imageUrl, required int index, required bool isLocal, }) { return Stack( children: [ ClipRRect( borderRadius: BorderRadius.circular(10), child: Container( decoration: BoxDecoration( color: const Color(0xFFFAFAFA), borderRadius: BorderRadius.circular(10), ), child: isLocal && image != null ? Image.file( image, fit: BoxFit.cover, width: double.infinity, height: double.infinity, ) : CachedNetworkImage( imageUrl: '$mediaUrl$imageUrl', fit: BoxFit.cover, width: double.infinity, height: double.infinity, placeholder: (context, url) => const Center( child: CircularProgressIndicator( strokeWidth: 2, color: Color(0xFFAAAAAA), ), ), errorWidget: (context, url, error) => Center( child: Image.asset( 'assets/images/icon_button.png', width: 40, height: 40, ), ), ), ), ), // Delete button Positioned( top: 4, right: 4, child: GestureDetector( onTap: () => _removeCarImage(index, isLocal), child: Container( padding: const EdgeInsets.all(4), decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), child: const Icon( Icons.close, color: Colors.white, size: 16, ), ), ), ), ], ); } }