import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; class MultiImagePicker extends StatelessWidget { final List imageUrls; final Function(int index, String imageUrl)? onImageTap; final Function(int index)? onDelete; final Function(String imageUrl)? onAdd; final bool enabled; final int maxImages; const MultiImagePicker({ Key? key, required this.imageUrls, this.onImageTap, this.onDelete, this.onAdd, this.enabled = true, this.maxImages = 2, }) : super(key: key); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Display existing images horizontally with add button Row( children: [ // Display existing images ...List.generate( imageUrls.length, (index) { return Padding( padding: EdgeInsets.only(right: 8.toScreenWidth), child: _buildImageItem(context, index), ); }, ), // Add image button (only show if under max limit) if (enabled && imageUrls.length < maxImages) _buildAddImageButton(context), ], ), ], ); } Widget _buildImageItem(BuildContext context, int index) { final imageUrl = imageUrls[index]; return Stack( alignment: AlignmentDirectional.topEnd, children: [ Container( height: 120, width: 120, decoration: BoxDecoration( color: AppColor.fieldBgColor(context), borderRadius: BorderRadius.circular(8), ), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: _getImageWidget(imageUrl), ), ).onPress(() { if (onImageTap != null) { onImageTap!(index, imageUrl); } else { _showFullImage(context, imageUrl); } }), if (enabled) Container( height: 32, width: 32, margin: const EdgeInsets.all(4), decoration: BoxDecoration( color: (context.isDark ? AppColor.neutral20 : AppColor.neutral40).withValues(alpha: 0.9), border: Border.all( color: (context.isDark ? AppColor.neutral20 : AppColor.neutral40).withValues(alpha: 0.7), ), borderRadius: BorderRadius.circular(20), ), child: const Icon(Icons.close, color: Color(0xffF63939), size: 20), ).onPress(() { if (onDelete != null) { onDelete!(index); } }), ], ); } Widget _buildAddImageButton(BuildContext context) { return GestureDetector( onTap: () => _pickImage(context), child: Container( height: 120, width: 120, decoration: BoxDecoration( color: AppColor.fieldBgColor(context), borderRadius: BorderRadius.circular(8), border: Border.all( color: context.isDark ? AppColor.neutral20 : AppColor.neutral30, width: 2, style: BorderStyle.solid, ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.add_photo_alternate, size: 32, color: context.isDark ? AppColor.white10 : AppColor.black10, ), 4.height, Text( 'Add Image', style: AppTextStyles.tinyFont.copyWith( color: context.isDark ? AppColor.white10 : AppColor.black10, ), ), ], ), ), ); } Widget _getImageWidget(String imageUrl) { // Check if it's base64 encoded (contains | separator) if (imageUrl.contains('|')) { try { final base64Data = imageUrl.split('|').last; return Image.memory( base64Decode(base64Data), fit: BoxFit.cover, ); } catch (e) { return const Icon(Icons.broken_image, size: 48); } } // Check if it's a local file path if (CMRequestUtils.isLocalUrl(imageUrl)) { return Image.file( File(imageUrl), fit: BoxFit.cover, ); } // Otherwise treat as network URL return Image.network( imageUrl, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon(Icons.broken_image, size: 48); }, ); } void _pickImage(BuildContext context) async { // Directly open camera - no bottom sheet final ImagePicker picker = ImagePicker(); final XFile? pickedFile = await picker.pickImage( source: ImageSource.camera, imageQuality: 70, maxWidth: 800, maxHeight: 800, ); if (pickedFile != null) { final bytes = await pickedFile.readAsBytes(); final base64Image = base64Encode(bytes); final fileName = pickedFile.path.split("/").last; final formattedImage = "$fileName|$base64Image"; // Call the onAdd callback to notify parent if (onAdd != null) { onAdd!(formattedImage); } } } void _showFullImage(BuildContext context, String imageUrl) { Navigator.of(context).push( MaterialPageRoute( builder: (_) => Scaffold( backgroundColor: Colors.black, body: SafeArea( child: Stack( children: [ Center( child: InteractiveViewer( child: _getImageWidget(imageUrl), ), ), const ABackButton(), ], ), ), ), ), ); } }