From e0d416c5a118216478d230c726862626135e4c0a Mon Sep 17 00:00:00 2001 From: Biftu Gadisa Date: Sat, 18 Oct 2025 21:25:27 -0400 Subject: [PATCH] Fix media picker: Replace mock implementations with actual device APIs - Updated media_picker.dart to use image_picker and file_picker packages for real camera/gallery/file access - Added iOS permissions (camera, microphone, photo library) to Info.plist - Added Android permissions (camera, audio, storage) to AndroidManifest.xml - Supports both legacy (API <33) and modern (API 33+) Android storage permissions --- android/app/src/main/AndroidManifest.xml | 10 ++++ ios/Runner/Info.plist | 9 ++++ lib/widgets/media_picker.dart | 63 ++++++++++++++++-------- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 52da9a7..b927487 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,14 @@ + + + + + + + + + UIApplicationSupportsIndirectInputEvents + + NSCameraUsageDescription + This app needs access to the camera to take photos and videos for sharing in chats. + NSMicrophoneUsageDescription + This app needs access to the microphone to record audio and video messages. + NSPhotoLibraryUsageDescription + This app needs access to your photo library to select photos and videos for sharing. + NSPhotoLibraryAddUsageDescription + This app needs permission to save photos and videos to your photo library. diff --git a/lib/widgets/media_picker.dart b/lib/widgets/media_picker.dart index f780b50..e10208b 100644 --- a/lib/widgets/media_picker.dart +++ b/lib/widgets/media_picker.dart @@ -42,7 +42,9 @@ library; import 'dart:io'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart' as picker; import '../services/chat_service.dart'; @@ -136,13 +138,20 @@ class _MediaPickerWidgetState extends State /// Calls onMediaSelected callback with the selected image file Future _pickImage(ImageSource source) async { try { - // Mock image picker implementation - // In production, use image_picker package - await Future.delayed(const Duration(milliseconds: 500)); + final imagePicker = picker.ImagePicker(); + final pickedFile = await imagePicker.pickImage( + source: source == ImageSource.camera + ? picker.ImageSource.camera + : picker.ImageSource.gallery, + imageQuality: 85, + maxWidth: 1920, + maxHeight: 1920, + ); - // For demonstration, create a mock image file - final mockImageFile = await _createMockFile('image.jpg', MessageType.image); - widget.onMediaSelected(mockImageFile, MessageType.image); + if (pickedFile != null) { + final file = File(pickedFile.path); + widget.onMediaSelected(file, MessageType.image); + } } catch (e) { _showError('Failed to pick image: $e'); } @@ -160,12 +169,18 @@ class _MediaPickerWidgetState extends State /// Calls onMediaSelected callback with the selected video file Future _pickVideo(VideoSource source) async { try { - // Mock video picker implementation - // In production, use image_picker package for videos - await Future.delayed(const Duration(milliseconds: 500)); + final imagePicker = picker.ImagePicker(); + final pickedFile = await imagePicker.pickVideo( + source: source == VideoSource.camera + ? picker.ImageSource.camera + : picker.ImageSource.gallery, + maxDuration: const Duration(minutes: 5), + ); - final mockVideoFile = await _createMockFile('video.mp4', MessageType.video); - widget.onMediaSelected(mockVideoFile, MessageType.video); + if (pickedFile != null) { + final file = File(pickedFile.path); + widget.onMediaSelected(file, MessageType.video); + } } catch (e) { _showError('Failed to pick video: $e'); } @@ -182,12 +197,15 @@ class _MediaPickerWidgetState extends State /// Calls onMediaSelected callback with the selected document file Future _pickDocument() async { try { - // Mock document picker implementation - // In production, use file_picker package - await Future.delayed(const Duration(milliseconds: 500)); + final result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['pdf', 'doc', 'docx', 'txt', 'rtf', 'xls', 'xlsx', 'ppt', 'pptx'], + ); - final mockDocFile = await _createMockFile('document.pdf', MessageType.document); - widget.onMediaSelected(mockDocFile, MessageType.document); + if (result != null && result.files.isNotEmpty) { + final file = File(result.files.single.path!); + widget.onMediaSelected(file, MessageType.document); + } } catch (e) { _showError('Failed to pick document: $e'); } @@ -202,12 +220,15 @@ class _MediaPickerWidgetState extends State /// Calls onMediaSelected callback with the selected audio file Future _pickAudio() async { try { - // Mock audio picker implementation - // In production, use file_picker package - await Future.delayed(const Duration(milliseconds: 500)); + final result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['mp3', 'wav', 'aac', 'm4a', 'ogg', 'opus'], + ); - final mockAudioFile = await _createMockFile('audio.mp3', MessageType.audio); - widget.onMediaSelected(mockAudioFile, MessageType.audio); + if (result != null && result.files.isNotEmpty) { + final file = File(result.files.single.path!); + widget.onMediaSelected(file, MessageType.audio); + } } catch (e) { _showError('Failed to pick audio: $e'); }