Flutter image_picker 插件实战指南:平台配置、相机委托与 1.0 API 迁移
2026/9/18 5:02:26 网站建设 项目流程

Flutter image_picker 插件实战指南:平台配置、相机委托与 1.0 API 迁移

【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages

本文以 Flutter 官方维护的image_picker插件(当前仓库内版本 1.2.3)为主线,系统讲解如何从系统相册挑选图片/视频、调用相机拍摄、处理 Android 低内存下 Activity 被销毁导致的数据丢失,以及桌面端通过cameraDelegate扩展相机能力,并完整覆盖从旧版PickedFileAPI 迁移到XFileAPI 的改造要点。读完本文,你将能够在一行代码内完成图片/视频的选取与拍摄,并为 iOS、Android 与桌面平台正确配置权限与运行环境。

插件概览与平台支持

image_picker是一个 Flutter 插件,用于"从系统相册挑选图片/视频,或使用相机拍摄新照片"。其主入口类为ImagePicker,所有能力都通过它暴露给业务代码。

从 pubspec.yaml 的插件声明可以看出,它是一个"联邦式(federated)"插件:主包本身不实现任何平台逻辑,而是按平台委托给独立实现包:

平台默认实现包
Androidimage_picker_android
iOSimage_picker_ios
Linuximage_picker_linux
macOSimage_picker_macos
Webimage_picker_for_web
Windowsimage_picker_windows

各平台的最低系统要求如下:

平台AndroidiOSLinuxmacOSWebWindows
支持SDK 24+iOS 13+任意10.15+详见 image_picker_for_web 的 Web 平台限制Windows 10+

底层架构上,image_picker依赖image_picker_platform_interface中定义的ImagePickerPlatform抽象类。该类默认持有MethodChannelImagePicker实例,平台实现通过ImagePickerPlatform.instance注册自己。这也是桌面端相机委托机制(下文会讲)能够生效的基础。

平台环境配置

iOS 配置:Info.plist 权限声明

0.8.1版本起,iOS 实现改用 PHPicker 在 iOS 14+ 上选取(多张)图片。随之而来的一个已知问题是:在 iOS 14+ 的模拟器上无法选取 HEIC 图片。建议在真机上测试,或改用非 HEIC 图片进行测试,直至 Apple 修复该问题。

需要在<project root>/ios/Runner/Info.plist中添加以下键:

  • NSPhotoLibraryUsageDescription—— 说明应用为何需要访问相册的权限,在 Xcode 可视化编辑器中显示为Privacy - Photo Library Usage Description
    • 如果你始终为requestFullMetadatafalse,系统不会请求该权限;但 App Store 审核政策要求 plist 中仍须包含此条目。
  • NSCameraUsageDescription—— 说明应用为何需要访问相机,显示为Privacy - Camera Usage Description
  • NSMicrophoneUsageDescription—— 如果你打算录制视频,需说明麦克风访问用途,显示为Privacy - Microphone Usage Description

Android 配置

Android 端无需任何额外配置即可开箱即用。历史上需要在AndroidManifest.xml<application>标签上添加android:requestLegacyExternalStorage="true",如今image_picker已迁移到 scoped storage(分区存储),该属性不再需要。

但强烈建议为以下两个场景做防御性处理:

处理 MainActivity 被系统销毁(Handling MainActivity destruction)

当系统内存压力较大时,Android 可能销毁正在使用 image_picker 的应用的 MainActivity。Android 端实现依赖默认的Intent.ACTION_GET_CONTENTMediaStore.ACTION_IMAGE_CAPTUREintent:intent 执行期间,源应用被移到后台并成为低内存清理的候选对象;intent 结束后 Android 会重启应用,但选取结果永远不会返回给原始调用方

此时应调用ImagePicker.retrieveLostData()找回丢失的数据。官方建议在每次应用启动时都执行该检查(该场景同样适用于pickMultiImagepickVideopickMedia,详见 image_picker.dart 的注释):

Future<void> getLostData() async { final picker = ImagePicker(); final LostDataResponse response = await picker.retrieveLostData(); if (response.isEmpty) { return; } final List<XFile>? files = response.files; if (files != null) { _handleLostFiles(files); } else { _handleError(response.exception); } }

从 lost_data_response.dart 的源码可以看到LostDataResponse的结构:它可能携带成功选取的file/files,也可能携带上次选取抛出的exception(注意:该异常是选取过程本身的异常,而非导致 MainActivity 被销毁的异常),type则区分是 image、video 还是 media。isEmptytrue表示没有丢失的数据。注意retrieveLostData()仅适用于 Android,在非 Android 平台调用会抛出UnimplementedError。完整流程可参考 example 应用。

永久保存图片和视频

用相机拍摄的图片/视频默认保存在应用本地缓存目录中,随时可能被系统清理,只能视为临时文件。如果需要永久保存,必须由开发者自行将其移动到更持久的位置(如应用文档目录或用户指定的存储位置)。

Android 13+ 的 Photo Picker

在 Android 13 及以上系统,本插件使用 Android 官方的 Android Photo Picker;在 Android 12 及以下,使用 Photo Picker 是可选的。具体接入方式见image_picker_android

避免使用launchMode: singleInstance

从设置了launchMode: singleInstance的 Activity 启动图片选择器,将总是返回RESULT_CANCELED。在该启动模式下,新 Activity 会被创建在独立的 Task 中,而不同 Task 之间的 Activity 无法通信,因此图片选择器无法把最终结果回传给调用方 Activity。解决办法是改用launchMode: singleTask

Windows、macOS 与 Linux:桌面端有限支持与相机委托

桌面端目前是有限支持:实现本质上是file_selector插件的一层包装,并预设了合适的文件类型过滤器。因此选择相关的修改选项(如最大宽高)尚不支持

默认情况下桌面端不支持ImageSource.camera(Android/iOS 之外没有系统级的拍照 UI)。但桌面实现允许通过设置cameraDelegate把相机调用委托给自定义实现,例如在main()中:

import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; // ··· class MyCameraDelegate extends ImagePickerCameraDelegate { @override Future<XFile?> takePhoto({ ImagePickerCameraDelegateOptions options = const ImagePickerCameraDelegateOptions(), }) async { return _takeAPhoto(options.preferredCameraDevice); } @override Future<XFile?> takeVideo({ ImagePickerCameraDelegateOptions options = const ImagePickerCameraDelegateOptions(), }) async { return _takeAVideo(options.preferredCameraDevice); } } // ··· void setUpCameraDelegate() { final ImagePickerPlatform instance = ImagePickerPlatform.instance; if (instance is CameraDelegatingImagePickerPlatform) { instance.cameraDelegate = MyCameraDelegate(); } }

设置cameraDelegate之后,使用ImageSource.cameraimage_picker调用就会正常地转发给你提供的委托。官方鼓励社区构建实现ImagePickerCameraDelegate的包,为桌面相机 UI 提供更多方案。

从 image_picker_platform.dart 的源码可以看到委托机制的实现细节:

  • CameraDelegatingImagePickerPlatformImagePickerPlatform的子类,持有可空的cameraDelegate
  • cameraDelegatenull时,调用ImageSource.camera会抛出StateError,并提示"需要 cameraDelegate";
  • 委托的takePhoto/takeVideo方法接收ImagePickerCameraDelegateOptions,其中包含preferredCameraDevice(默认后置摄像头)与maxVideoDuration(录视频最大时长,默认无限制),见 camera_delegate.dart;
  • 同时supportsImageSource(ImageSource.camera)在未设置 delegate 时返回false——业务代码可先用该方法探测当前平台是否支持相机,避免运行时抛错。
macOS 额外配置

由于 macOS 实现基于file_selector,需要为应用添加文件系统访问 entitlement(在 macOS 的 entitlements 文件中):

<key>com.apple.security.files.user-selected.read-only</key> <true/>

核心 API 用法

快速上手示例

以下代码覆盖了单图/多图、单视频/多视频、图片与视频混合选取的全部入口,与 readme_excerpts.dart 中的示例一致:

final picker = ImagePicker(); // 从相册选一张图片。 final XFile? image = await picker.pickImage(source: ImageSource.gallery); // 用相机拍一张照片。 final XFile? photo = await picker.pickImage(source: ImageSource.camera); // 从相册选一个视频。 final XFile? galleryVideo = await picker.pickVideo(source: ImageSource.gallery); // 用相机录一段视频。 final XFile? cameraVideo = await picker.pickVideo(source: ImageSource.camera); // 一次选多张图片。 final List<XFile> images = await picker.pickMultiImage(); // 选单个图片或视频。 final XFile? media = await picker.pickMedia(); // 选多个图片和视频。 final List<XFile> medias = await picker.pickMultipleMedia();

所有返回的XFile均来自 cross_file 包,统一了跨平台文件抽象。注意:返回的XFile只在单次应用会话内有效,不要跨会话保存其文件路径(参见 image_picker.dart 中每个方法的注释)。

ImageSource与完整参数详解

ImageSource枚举只有两个取值,定义见 image_source.dart:

  • camera—— 打开设备相机,让用户拍一张新照片/录一段新视频;
  • gallery—— 打开用户相册。
pickImage参数
Future<XFile?> pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, bool requestFullMetadata = true, })
参数类型默认值说明
sourceImageSource必填图片来源:相机或相册
maxWidthdouble?null图片最大宽度(像素)。指定后图片会被等比缩放到不超过该宽度;与maxHeight同时为空时保持原尺寸
maxHeightdouble?null图片最大高度(像素),规则同上
imageQualityint?null压缩质量,0–100,100 为原始质量。null表示返回原始质量
preferredCameraDeviceCameraDeviceCameraDevice.rear相机来源为camera时优先使用的前/后置摄像头;相册来源时被忽略;设备不支持时也被忽略。注意 Android 的 intent 没有公开参数指定前后摄像头,因此该功能在 Android 上不保证生效
requestFullMetadatabooltrue是否请求完整图片元数据。设为true时可能触发额外权限请求(如 iOS 的相册权限);始终传false则 iOS 不会请求NSPhotoLibraryUsageDescription权限

参数校验规则(来自 image_options.dart 的_validateOptions):

  • imageQuality必须在 0–100 之间,否则抛ArgumentError
  • maxWidth/maxHeight不能为负数,否则抛ArgumentError

HEIC 兼容性:iOS 支持 HEIC 图片,而 Android 8 及以下不支持;Android 9 及以上只有在配合尺寸修改(指定maxWidth/maxHeight)时才支持 HEIC。压缩(imageQuality)仅对部分格式有效——如 JPEG,以及 Android 平台上的 PNG 和 WebP;若选取的图片格式不支持压缩,插件会打印警告日志。

pickVideo参数
Future<XFile?> pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, })
  • source—— 必填,同pickImage
  • preferredCameraDevice—— 默认CameraDevice.rear,规则同pickImage
  • maxDuration—— 仅在相机拍摄(ImageSource.camera)时生效,限制视频最大时长;为null时不限时长;从相册选取视频时该参数被忽略。
pickMultiImage/pickMultipleMedia/pickMultiVideo

三者均支持limit参数限定最大可选数量,且各平台可能忽略该值pickMultiImagepickMultipleMedialimit小于 1 时抛ArgumentError;当limit == 1时,源码会内部转调单选的pickImage/pickMedia并包装成单元素列表(见 image_picker.dart 与pickMultipleMedia的实现)。pickMultiVideo还支持maxDuration限制视频时长。另外,pickMultiImagepickMediapickMultipleMediapickMultiVideo不支持 iOS 14 以下版本

异常情况

pick*系列方法可能抛出PlatformException,常见触发场景包括:应用没有相机/相册权限、设备无可用相机、插件已被占用(上一次选取尚未结束)、iOS 端临时文件创建失败、Android 端插件 Activity 分配失败,以及未知错误。业务代码应对这些异常做兜底处理。

迁移到 1.0:从PickedFileXFile

0.8.2版本起,image_picker 新增了返回XFile(来自 cross_file 包)的方法,替代插件自有的PickedFile。旧方法在 0.8.9 之前一直保持兼容,并在 1.0.0 中被移除

在 image_picker_platform.dart 中可以看到,旧的pickImagepickMultiImagepickVideoretrieveLostData等方法均被标记为@Deprecated,并抛出UnimplementedError,即当前版本中这些接口已经不再可用。

迁移对照表:

旧 API(已移除)新 API(当前使用)
PickedFile image = await _picker.getImage(...)XFile image = await _picker.pickImage(...)
List<PickedFile> images = await _picker.getMultiImage(...)List<XFile> images = await _picker.pickMultiImage(...)
PickedFile video = await _picker.getVideo(...)XFile video = await _picker.pickVideo(...)
LostData response = await _picker.getLostData()LostDataResponse response = await _picker.retrieveLostData()

升级到 1.x 时,只需将返回值类型从PickedFile替换为XFile、方法名从get*替换为pick*,并注意LostDataResponse相比旧LostData增加了files(多选时丢失的文件列表)与type(image/video/media 类型)等字段,isEmpty表示无丢失数据。同时,1.0 之后新增了pickMediapickMultipleMediapickMultiVideo等混合媒体选取能力,以及桌面端cameraDelegate扩展点,均可在 ImagePicker 类中直接使用。

小结

image_picker以一行pick*调用覆盖了相册选取、相机拍摄、多选与混合媒体等主流场景,是 Flutter 官方插件体系中媒体入口的标准选择。落地时需重点关注三点:iOS 的权限 plist 声明(NSPhotoLibraryUsageDescription/NSCameraUsageDescription/NSMicrophoneUsageDescription)、Android 低内存下 MainActivity 被销毁时的retrieveLostData()启动检查,以及桌面端通过cameraDelegate补齐相机能力。相关可运行示例见 example 应用,各平台实现与平台接口可分别在packages/image_picker/目录下的对应子包中查阅。

【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询