From maui-skills
Guides .NET MAUI developers on MediaPicker for selecting/capturing photos/videos, multi-select (.NET 10), MediaPickerOptions, permissions, FileResult handling, pitfalls, and platform specifics (Android/iOS).
How this skill is triggered — by the user, by Claude, or both
Slash command
/maui-skills:maui-media-pickerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Single-select returns `null`, multi-select returns an empty list. Not checking causes `NullReferenceException`.
Single-select returns null, multi-select returns an empty list. Not checking causes NullReferenceException.
// ❌ Crashes when user cancels
var photo = await MediaPicker.Default.PickPhotoAsync();
using var stream = await photo.OpenReadAsync();
// ✅ Always check for null / empty
var photo = await MediaPicker.Default.PickPhotoAsync();
if (photo is null) return;
using var stream = await photo.OpenReadAsync();
All MediaPicker methods must run on the UI thread or they throw.
// ❌ Will throw on some platforms
await Task.Run(async () => await MediaPicker.Default.PickPhotoAsync());
// ✅ Call directly from a command or event handler on the UI thread
var photo = await MediaPicker.Default.PickPhotoAsync();
Devices without cameras (emulators, some tablets) will throw if you call capture methods.
// ❌ Crashes on devices without a camera
var photo = await MediaPicker.Default.CapturePhotoAsync();
// ✅ Guard with capability check
if (MediaPicker.Default.IsCaptureSupported)
{
var photo = await MediaPicker.Default.CapturePhotoAsync();
}
OpenReadAsync() returns a stream that must be disposed.
// ❌ Leaks the stream
var stream = await photo.OpenReadAsync();
var bytes = ReadAllBytes(stream);
// ✅ Dispose with using
using var stream = await photo.OpenReadAsync();
MediaPickerOptions.SelectionLimit is advisory. Always validate the count yourself:
var options = new MediaPickerOptions { SelectionLimit = 5 };
var results = await MediaPicker.Default.PickPhotosAsync(options);
if (results.Count() > 5)
{
// Warn user or take only the first 5
results = results.Take(5);
}
READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGEREAD_MEDIA_IMAGES / READ_MEDIA_VIDEO (broad storage permissions are ignored)android:maxSdkVersion="32" on the old permissions to avoid Play Store warningsMissing any one of NSCameraUsageDescription, NSMicrophoneUsageDescription,
NSPhotoLibraryUsageDescription, or NSPhotoLibraryAddUsageDescription causes
a runtime crash when that feature is accessed.
<queries> for camera intentsWithout the IMAGE_CAPTURE query in AndroidManifest.xml, CapturePhotoAsync
may fail silently on Android 11+ due to package visibility restrictions.
| Scenario | Method |
|---|---|
| User picks one photo | PickPhotoAsync() |
| User picks multiple (.NET 10+) | PickPhotosAsync() with SelectionLimit |
| App needs camera capture | Check IsCaptureSupported → CapturePhotoAsync() |
| Save picked file permanently | Copy stream to FileSystem.AppDataDirectory |
| Need photo metadata | Set PreserveMetaData = true in MediaPickerOptions |
IsCaptureSupported guard before capture methodsusingmaxSdkVersion<queries> block for camera intentInfo.plist has all four usage description keysSelectionLimit validated in code, not trusted from platformnpx claudepluginhub davidortinau/maui-skills --plugin maui-skillsGuides FilePicker APIs, FileSystem helpers, bundled assets, and app data storage in .NET MAUI apps. Covers platform permissions, file formats, and pitfalls on Android, iOS, macOS, Windows.
Photo picker, camera capture, and media handling using PhotosPicker, PHPickerViewController, AVCaptureSession, and PhotoKit in iOS Swift apps.
Provides API reference for PhotosUI including PhotosPicker for SwiftUI photo selection and PHLivePhotoView. Useful for iOS photo picker implementation.