media_picker_plus 1.1.0-rc.15
media_picker_plus: ^1.1.0-rc.15 copied to clipboard
A Flutter plugin to pick or capture images and videos with quality control options
1.1.0-rc.14 #
Fixed #
- Pick video / pickMedia result type: Method channel now accepts both
StringandMapfrom native. When native returns a map (e.g.{'path': '...'}), the path is extracted sopickVideo()and other pick flows no longer throw due to type mismatch. - iOS error propagation: iOS now returns
FlutterError(not{error:{code,message}}maps) for picker failures likesave_failed, ensuring Dart receives a properPlatformExceptioninstead of an “unknown map structure”.
1.1.0-rc.13 #
Added #
- Multi-image capture from camera with live preview: Added
captureMultiplePhotos()method with native camera preview for instant multi-capture. The screen displays a live camera preview using native platform views, with instant capture button that directly takes photos (no picker UI). Thumbnails appear in the bottom strip with badge count showing progress. Each image is processed with quality and watermark settings (crop is skipped for multi-image). No external camera package dependencies - uses only native camera APIs (AVCaptureSession on iOS, CameraX on Android). MultiImageOptions: New options class controllingmaxImages,minImages, andconfirmOnDiscardfor multi-image flows.MultiCaptureScreen: Native camera preview screen with live feed embedded via platform views (UiKitView/AndroidView). Features:- Zoom controls: 0.5x, 1x, 2x, 3x zoom levels displayed at the top of the bottom control area. Fully functional with native camera zoom implementation on both iOS (AVCaptureDevice videoZoomFactor) and Android (CameraX setZoomRatio).
- Grouped thumbnails: Stacked thumbnail preview on bottom-left corner showing latest captured photo with badge count. Tap to view all photos in a grid.
- Capture button: Large centered button for instant photo capture
- Camera switch: Flip button on bottom-right corner to switch between front and back cameras
- Grid view: Tap the thumbnail group to see all captured photos in a 3-column grid with delete options
- Thumbnail strip at bottom, badge count showing progress, instant capture button, and full-screen preview for captured photos
- Native camera platform views:
- iOS:
CameraView.swiftusing AVCaptureSession, AVCapturePhotoOutput, and AVCaptureVideoPreviewLayer with customCameraPreviewViewthat properly handles layout updates - Android:
CameraView.ktusing CameraX (camera-camera2, camera-lifecycle, camera-view) with proper Activity lifecycle binding - Method channel for Flutter-native communication (
info.thanhtunguet.media_picker_plus/camera) - Preview layer automatically resizes when view bounds change to ensure proper display
- iOS:
MultiImageHelper: Orchestration class for camera multi-capture and gallery multi-pick with batch processing.- Cross-platform thumbnail widget:
thumbnail_image.dartwith conditional imports for IO (Image.filewithcacheWidth) and web (Image.network). - Added "Multi-Capture Photos" demo button to example app.
- Added
doc/multi-image-capture.mdfeature documentation. - Added
doc/multi-capture-ui-layout.mddetailed UI layout documentation with implementation guide for zoom functionality.
Fixed #
- iOS camera preview: Fixed black screen issue by creating custom
CameraPreviewViewthat updates preview layer frame inlayoutSubviews()when bounds change - Android camera lifecycle: Fixed camera initialization by properly passing Activity reference to CameraX via activity provider lambda, ensuring correct lifecycle binding
- 0.5x zoom uses ultrawide back lens: Updated native multi-capture camera implementations to switch to back ultrawide hardware for
0.5xwhen available, and switch back to standard wide lens for1x+. Added safe fallback to standard back lens on devices without ultrawide support. - Camera switch button in multi-capture screen: Fixed front/back switching by keying native platform camera views with the selected device, forcing recreation with updated
preferredCameraDeviceparams. captureMultiplePhotos()web/macOS compatibility fallback: Avoids not-implemented flows by falling back to a single camera capture on web and macOS, returning a one-itemList<String>to keep API shape consistent.- Timestamp collision causing duplicate file paths: Fixed bug where processing multiple media files (images/videos) within the same second would result in duplicate paths due to filename collisions. Updated all timestamp formats across Android, iOS, and macOS from second-precision to millisecond-precision. This affects: image picking (
pickMultipleImages), video picking (pickMultipleMedia), watermarking (image/video), thumbnails, compression, and camera capture. All processed files now useyyyyMMdd_HHmmss_SSSformat (orInt(Date().timeIntervalSince1970 * 1000)) ensuring unique filenames even with rapid successive operations. Refactored to use reusable timestamp methods: Android usesgenerateTimestamp()companion object method; iOS usesSwiftMediaPickerPlusPlugin.generateTimestamp()andgenerateTimestampMillis()static methods; macOS usesMediaPickerPlusPlugin.generateTimestamp()andgenerateTimestampMillis()static methods;CameraViewhas its owngenerateTimestamp()static method. - Picker cancellation returns
nullconsistently: Updated picker/capture dialog cancellation behavior to returnnull(instead of throwing errors) across Android, iOS, and macOS picker/capture cancel paths, aligning with existing nullable API contracts (Future<String?>,Future<List<String>?>) and web behavior. Kept non-picker failures (permissions/processing errors) as exceptions.
1.1.0-rc.11 #
Added #
- Preferred camera device hint: Added
preferredCameraDevicetoMediaOptionsto request front/back camera for capture on Android/iOS. This is best-effort and may be ignored by Android camera apps; it is currently ignored on web and macOS. - Added Zed Flutter debugging configuration (
.zed/debug.json) and setup notes indoc/zed-debugging.md.
Fixed #
- Web
pickFile/pickMultipleFilesnow treat all selected files as generic files instead of validating as videos. - Removed
dart:iousage from crop UI flows on web by adding conditional file/image loaders. - Guarded
CropUIasync state updates withmountedchecks and expanded repaint conditions to handle layout changes.
1.1.0-rc.9 #
Fixed #
- Move Android image/video processing after capture or selection to background threads to avoid blocking the UI during compression or watermarking.
- Android thread safety for media options: Fixed race condition where background threads processing images/videos could read stale or incorrect
mediaOptionsif another request updated the global mutable state while processing was in progress:- Created
processVideoWithOptionsfunction that accepts options as a parameter (similar to existingprocessImageWithOptions) - Updated
processVideoto snapshotmediaOptionsand delegate toprocessVideoWithOptions - Updated
processVideoWithFFmpegandwatermarkVideoWithNativeProcessingto accept options parameter instead of reading from globalmediaOptions - Updated all
onActivityResultcall sites to snapshotmediaOptionsbefore starting background threads, ensuring each request uses its own immutable copy of options - Added comprehensive tests for concurrency scenarios on both Android native and Dart sides
- Created
- iOS build errors: Fixed Swift compiler errors in iOS test pipeline:
- Fixed iOS 14+ availability check for
.limitedauthorization status inhasGalleryPermission()andrequestGalleryPermission()methods to support iOS versions below 14.0 - Fixed type mismatch errors in division operations by converting
InttoDoublebefore dividing by100.0inprocessImage()andaddWatermarkToExistingImage()methods
- Fixed iOS 14+ availability check for
- Android release build errors: Fixed compilation error in Android release builds where
integration_testplugin (dev dependency) was being included inGeneratedPluginRegistrant.java. Added Gradle task to automatically removeintegration_testplugin registration from generated files before release compilation. - Test timeouts: Fixed timeout issues in
CropUItests by properly waiting for post-frame callbacks. Tests now pump frames correctly and wait for theonCropChangedcallback to be triggered after widget initialization. - iOS build errors: Fixed Swift compiler errors in iOS example app where
FlutterImplicitEngineDelegateandFlutterImplicitEngineBridgetypes were not found. SimplifiedAppDelegate.swiftto use standardFlutterAppDelegatepattern without implicit engine delegate.
1.1.0-rc.8 #
Added #
- Issue-focused tests across Flutter, web, and native targets to capture current behavior.
Fixed #
- Android video compression rotation: Fixed aspect ratio calculation in
compressVideoto account for video rotation metadata. Videos with 90° or 270° rotation now correctly use effective (post-rotation) dimensions for scaling calculations, preventing incorrect output dimensions when FFmpeg auto-rotates frames. - Android thread safety: Fixed race conditions when processing media from background threads:
- Added
@Volatileannotation tomediaOptionsfor thread-safe reads - Created
processImageWithOptionshelper that takes explicit options instead of relying on global mutable state - Fixed
extractThumbnailto use the new thread-safe helper instead of mutating globalmediaOptions - Removed unnecessary
mediaOptionsmutation inprocessVideothat created a race condition with no benefit
- Added
- iOS thread safety: Fixed race condition in
PHPickerViewControllerDelegatewhere background closures could read stalemediaOptionsif another method channel call changed it:- Created overloaded
saveMediaToFile(info:options:)that takes explicit options parameter - Updated PHPicker delegate to capture
mediaOptionsat the start and pass it explicitly to background closures
- Created overloaded
- iOS build errors: Fixed Swift compiler errors in
addWatermarkToVideoCompositionmethod usage:- Fixed incorrect parameter label
renderSize:(should bevideoSize:) - Fixed incorrect use of optional binding for non-optional return type
- Fixed incorrect parameter label
- Documented known issues and added TODO markers for follow-up fixes.
- Fixed TODO-tracked issues across iOS gallery permissions, image quality handling, Android thumbnail extraction, video processing, and gallery permissions.
1.1.0-rc.7 #
Fixed #
- Video rotation on all platforms: Fixed video rotation issue where videos captured by the camera were incorrectly rotated from portrait to landscape during processing.
- iOS/macOS: The
applyVideomethod now properly uses the video track'spreferredTransformmatrix instead of a simplified rotation check, ensuring captured videos maintain their original orientation. - Android: Fixed dimension calculations to use effective (post-rotation) dimensions. Removed explicit rotation filters from FFmpeg commands since FFmpeg auto-rotates based on metadata. Updated
applyVideo,processVideo, andwatermarkVideoWithNativeProcessingto correctly handle rotated videos.
- iOS/macOS: The
Changed #
- Example fullscreen image viewer now displays basic image metadata (width, height, and configured quality).
- Example fullscreen video viewer now displays video metadata (width, height, and quality).
- Generated thumbnails in the example app are now clickable and can be opened in the fullscreen image viewer.
1.1.0-rc.5 #
Fixed #
- Android watermark text now supports line breaks ("\n") consistently across images and videos.
1.1.0-rc.4 #
Fixed #
- WebAssembly compatibility: Fixed all WebAssembly compatibility issues to enable WASM builds for web platform:
- Fixed JS interop type casting issues by replacing unsafe
as JSAnycasts with proper.toJSmethod calls - Converted async event handlers to synchronous ones since WebAssembly doesn't support async functions in JS interop
- Fixed canvas context property assignments (
fillStyle,strokeStyle) to use proper JS interop conversion - Replaced unsafe JS interop value casting with proper
(value as JSAny).dartify() as Stringconversions - Fixed runtime type checking with JS interop types to avoid platform-inconsistent behavior
- Fixed JS interop type casting issues by replacing unsafe
- Web builds now compile successfully with WebAssembly support enabled
1.0.0 #
Added #
- Video compression: Added
compressVideo()method to compress video files with customizable quality, resolution, and bitrate settings. Includes industry-standard quality presets (360p, 480p, 640p, 720p, 1080p, 1280p, 1440p, 1920p, 2K, Original) with aspect ratio preservation and optimized bitrates. Features progress callbacks and option to delete original files after compression. Supported on Android (MediaCodec), iOS/macOS (AVFoundation), with web implementation planned for future release. - Video thumbnail extraction: Added
getThumbnail()method to extract thumbnail images from video files at specified times. Supports customizable extraction time (default 1 second), resizing options, quality control, and watermarking. Works on all platforms (Android, iOS, macOS, Web) using FFmpeg on native platforms and HTML5 Canvas API on web. - Comprehensive example app (
example/lib/main.dart) demonstrating permissions, picking, cropping, watermarking, andcamerawesomeintegration. - Fullscreen viewer for selected media in example app: Users can now tap on displayed images or videos to view them in fullscreen mode with pinch-to-zoom functionality using
InteractiveViewer. - macOS camera preview: Added native camera preview window for photo and video capture on macOS using
AVCaptureVideoPreviewLayer. Users now see a live camera preview with manual capture controls (Capture/Record buttons) similar to iOSUIImagePickerController, replacing the previous silent automatic capture behavior. - Web permission documentation: Added comprehensive guide (
docs/web-permissions.md) explaining how browser permissions work on web, whypermission_handlerdoesn't support web, and how to properly handle permissions across platforms. - Web platform considerations in README: Added prominent section in README warning developers about web-specific requirements: checking
kIsWebbefore usingPlatformAPIs, usingImage.network()for images, usingVideoPlayerController.networkUrl()for videos, and proper permission handling. Includes complete code examples for all common scenarios.
Fixed #
- Web permission handling: Fixed example app attempting to use
Permission.photos,Permission.camera, andPermission.microphoneon web platform where they are not supported. The example app now correctly skips permission requests on web (where browser handles permissions automatically) and desktop platforms (where permissions are configured via Info.plist/manifest). Only mobile platforms (Android/iOS) now request runtime permissions. - Web image display: Fixed
Image.file()assertion error on web platform by implementing platform-aware image loading. The example app now usesImage.network()for data URLs and blob URLs on web, andImage.file()for file paths on native platforms. Added helper function_buildImage()demonstrating the recommended pattern for cross-platform image display. - Web video playback: Fixed "Unsupported operation: Platform._operatingSystem" error when playing videos on web. The example app now uses
VideoPlayerController.networkUrl()for data/blob URLs on web, andVideoPlayerController.file()for file paths on native platforms. - Web path display: Improved example app to show user-friendly path descriptions (e.g., "Image (data URL)") instead of displaying long base64 strings on web platform. Native platforms continue to show full file paths.
- Watermark font consistency: Fixed video watermarks to match photo watermark styling on iOS and macOS. Video watermarks now use attributed strings with black stroke/outline (strokeWidth: -2), creating consistent white text with black outline across both images and videos.
- Android video watermarking: Fixed FFmpeg command incompatibility with ffmpeg-kit-https variant. Changed from libx264 encoder with preset options to mpeg4 encoder with quality parameter (-q:v 5), resolving "Unrecognized option 'preset'" error that prevented video watermarking on Android.
- Android captured video watermarking: Added fallback to resolve recorded video paths from camera content URIs when the app-provided file is empty, ensuring watermarking runs for camera captures.
- Android video watermark styling: Removed the semi-transparent background from video watermark bitmaps so Android video watermarks match photo styling and iOS appearance.
- Android video watermark positioning: Adjusted watermark coordinates for rotated portrait videos so right-side placements stay visible.
0.3.0-rc.5 #
Added #
- Unit/widget tests for crop options and interactive crop UI.
- Integration tests (mocked MethodChannel) for camera/gallery/file flows under
example/integration_test/. CropUInow supportsinitialImageinjection (useful for tests/advanced usage).- Standalone watermarking methods:
MediaPickerPlus.addWatermarkToImage()andMediaPickerPlus.addWatermarkToVideo()for adding watermarks to existing media files. - Watermark feature example: Added comprehensive watermark feature demonstration in
example/lib/features/watermark_feature.dartwith interactive UI for adding watermarks to photos and videos. - Third-party camera integration demo: Added example showing how to integrate third-party camera packages with MediaPickerPlus watermarking functionality.
- Percentage-based watermark font size: Added
watermarkFontSizePercentageparameter toMediaOptionsfor responsive watermark sizing. Font size is calculated as a percentage (default 4%) of the shorter edge of the image/video, ensuring consistent watermark appearance across different media dimensions. Updated all platform implementations (Android, iOS, Web) to support this feature.
Fixed #
- Fix iOS image watermark positioning so it no longer overflows the image edge (more consistent with Android).
- Crop UI now emits the initial crop rectangle reliably (avoids first callback being throttled).
- Fix black image output issue in iOS
addWatermarkToImagemethod by improving graphics context configuration.
0.1.2+4 #
Fixed #
- Refactored example project for easier maintainance
- Fixed bug: cropping image outputed wrong result image (iOS)
- Fixed bug: picking image on web
- Fixed bug: error picking file on Android