fluttercandies/flutter_photo_manager
fluttercandies/flutter_photo_manager has 771 GitHub stars and 393 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-10 03:29 UTC. Last push 2026-10-23.
A Flutter plugin that provides images, videos, and audio abstraction management APIs without interface integration, available on Android, iOS, macOS and OpenHarmony.
可以做什么
- iOS/macOS: updates PHAsset.creationDate.
- Android Q (API 29)+: updates the MediaStore DATE_TAKEN field (unsupported on API 28 and below).
- clearFileCache - ❌
- requestCacheAssetsThumbnail - ❌
实测星标增长
- 24小时
- 0
- 7天
- 更新延迟
- 15天
- 更新延迟
- 1个月
- 更新延迟
各周期内的星标净变化,周期相互重叠,并非每日新增。缺失的测量保持空白。星标代表关注度,不代表软件质量。
README-ZH.md
原文photo_manager
English | 中文
通过相册的抽象 API 对设备中的资源(图片、视频、音频)进行管理,不需要集成 UI。 在 Android、iOS、macOS and OpenHarmony上可用。
集成此插件的推荐项目
| name | pub | github |
|---|---|---|
| wechat_assets_picker | ||
| wechat_camera_picker |
关于此插件的文章
破坏性改动迁移指南
查看 迁移指南 了解如何在破坏性改动之间迁移。
目录列表
常见问题
你可以在 GitHub issues 上搜索到经常遇到的问题,比如构建错误,运行时异常等等。
使用前准备
添加依赖
有两种方式可以把依赖添加到你的项目中:
- (推荐) 运行
flutter pub add photo_manager. - 或者直接添加到项目的
pubspec.yaml中的dependencies部分:
dependencies:
photo_manager: $latest_version
导入到你的项目中:
import 'package:photo_manager/photo_manager.dart';
原生平台的配置
最低的平台版本: Android API 16, iOS 9.0, macOS 10.15.
[!NOTE] iOS 9.0 的最低版本适用于 CocoaPods 构建。使用 Swift Package Manager 构建时, iOS 最低版本为 13.0,以匹配较新 Flutter SDK 生成的
FlutterFrameworkSwiftPM 包所要求的 iOS 13.0。macOS 最低版本(10.15)两者相同。
- Android:Android 配置准备.
- iOS:iOS 配置准备.
- macOS:与 iOS 几乎一致。
Android 配置准备
Kotlin, Gradle, AGP
该插件使用 Kotlin 1.7.22 来构建。
如果你的项目使用了低于此版本的 Kotlin/Gradle/AGP,请升级到大于或等于指定版本。
更具体的做法:
- 更新你的 Gradle version (
gradle-wrapper.properties) 到7.5.1或者最新版本。 - 更新你的 Kotlin version (
ext.kotlin_version) 到1.7.22或者最新版本。 - 更新你的 AGP version (
com.android.tools.build:gradle) 或者7.2.2或者最新版本。
Android 10 (Q, 29)
如果你没有设置 compileSdkVersion 或 targetSdkVersion 为 29,你可以跳过本节。
Android 10 引入了 Scoped Storage,导致原始资源文件不能通过其文件路径直接访问。
如果你的 compileSdkVersion 或 targetSdkVersion 为 29,
为了能够成功获取到媒体资源,你可以考虑通过在 AndroidManifest.xml
中添加 android:requestLegacyExternalStorage="true",如下所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fluttercandies.photo_manager_example">
<application
android:label="photo_manager_example"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
</application>
</manifest>
注意: 这样设置的应用无法上架 Google Play。
这不是必须的,插件缓存文件的时候还是可以正常工作的。
但是开发者需要主动清理缓存,最佳实践是启动应用时调用
PhotoManager.clearFileCache 清理缓存文件。
Glide
本插件使用 Glide 在 Android 平台上创建缩略图。
如果你发现 Glide 出现了一些警告日志,说明主项目需要实现 AppGlideModule。
请查看 Generated API 的相关文档说明。
iOS 配置准备
添加 NSPhotoLibraryUsageDescription 到你的项目的 ios/Runner/Info.plist 中:
<key>NSPhotoLibraryUsageDescription</key>
<string>In order to access your photo library</string>
在 iOS 11 或者更高版本中,如果你只需要请求相册的写入权限,
仅需要添加 NSPhotoLibraryAddUsageDescription 到你的项目的 ios/Runner/Info.plist。

使用方法
请求权限
大部分的 API 只在获取到权限后才能正常使用。
final PermissionState ps = await PhotoManager.requestPermissionExtend();
if (ps.isAuth) {
// 已获取到权限
} else if (ps.hasAccess) {
// 已获取到权限(哪怕只是有限的访问权限)。
// iOS Android 目前都已经有了部分权限的概念。
} else {
// 权限受限制(iOS)或者被拒绝,使用 `==` 能够更准确的判断是受限还是拒绝。
// 你可以使用 `PhotoManager.openSetting()` 打开系统设置页面进行进一步的逻辑定制。
}
如果你确定你的应用已经授予了权限,你也可以忽略权限的检查:
PhotoManager.setIgnorePermissionCheck(true);
对于一些后台操作(应用未启动等)而言,忽略检查是比较合适的做法。
同时你还可以调用 PhotoManager.getPermissionState 来获取权限状态。
请确保调用该方法使用的权限设置与调用其他方法的权限设置相同。
受限的资源权限
iOS 受限的资源权限
iOS14 引入了部分资源限制的权限 (PermissionState.limited)。
PhotoManager.requestPermissionExtend() 会返回当前的权限状态 PermissionState。
详情请参阅 PHAuthorizationStatus。
如果你想要重新选择在应用里能够读取到的资源,你可以使用 PhotoManager.presentLimited() 重新选择资源,
这个方法对于 iOS 14 以上的版本生效。
如果你想要禁止每次应用重新启动后访问媒体时自动弹出提示,
你可以将 Info.plist 的 Prevent limited photos access alert 设置为 YES
(或者像下面一样手动编写):
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
<true/>
Android 受限的资源权限
与 iOS 类似,Android 14 (API 34) 中也引入了这个概念。
它们在行为上略有不同(基于模拟器):
在 Android 中一旦授予某个资源的访问权限,就无法撤销,
即使再次使用 presentLimited 时不选中也不会撤销对它的访问权限。
受限授权与请求类型不匹配时的处理
无论 iOS 还是 Android,系统都不会告知当前有限访问的选择集里包含哪些媒体类型。
如果应用请求了 RequestType.image,但用户在授予有限访问时只勾选了视频,
权限仍然会返回 PermissionState.limited,查询图片时也会合理地返回 0 条结果,
和"库里确实没有图片"无法区分。
推荐的处理方式是在这种情况下引导用户重新选择:
if (state == PermissionState.limited) {
// Android 14+ 上 `type` 会限制 picker 只显示对应类型;iOS 不支持按类型过滤,此参数会被忽略。
await PhotoManager.presentLimited(type: RequestType.image);
}
获取相簿或图集 (AssetPathEntity)
相簿或者图集以抽象类 AssetPathEntity 的形式呈现,
在 Android 中它表示为具有相同 bucketId 的 MediaStore 记录的集合,
在 iOS/macOS 中则是 PHAssetCollection 的记录。
获取所有相册:
final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList();
详情请参阅 getAssetPathList。
getAssetPathList 方法的参数
| 参数名 | 说明 | 默认值 |
|---|---|---|
| hasAll | 如果你需要一个包含所有资源(AssetEntity) 的 PathEntity ,传入 true | true |
| onlyAll | 如果你只需要一个包含所有资源的,传入true | false |
| type | 资源文件的类型(视频、图片、音频) | RequestType.common |
| filterOption | 用于筛选 AssetEntity,详情请参阅 过滤资源 | FilterOptionGroup() |
| pathFilterOption | 只对 iOS 和 macOS生效,对应原生中的相册类型,详情请参阅 PMPathFilterOption。 | 默认为包含所有 |
PMPathFilterOption (2.7.0 新增)
[!NOTE] 当前仅在 iOS 和 macOS 上可用.
final List<PMDarwinAssetCollectionType> pathTypeList = []; // 配置为你需要的类型
final List<PMDarwinAssetCollectionSubtype> pathSubTypeList = []; // 配置为你需要的子类型
final darwinPathFilterOption = PMDarwinPathFilter(
type: pathTypeList,
subType: pathSubTypeList,
);
PMPathFilter pathFilter = PMPathFilter();
PMDarwinAssetCollectionType的枚举值一一对应 PHAssetCollectionType | 苹果官网文档.
PMDarwinAssetCollectionSubtype 的枚举值一一对应 PHAssetCollectionSubType | 苹果官网文档.
获取资源 (AssetEntity)
资源(图片/视频/音频)以 AssetEntity 的方式呈现,
它抽象了原生中关于媒体对象的一系列属性和方法。
在 Android 中表示 MediaStore 记录的其中一些字段的集合,
在 iOS/macOS 则是 PHAsset 的记录。
通过 AssetPathEntity 获取
你可以通过 分页方法 获取:
final List<AssetEntity> entities = await path.getAssetListPaged(page: 0, size: 80);
也可以通过 范围索引方法 获取:
final List<AssetEntity> entities = await path.getAssetListRange(start: 0, end: 80);
通过 PhotoManager 方法 (2.6.0+) 获取
首先你需要获取资源的数量:
final int count = await PhotoManager.getAssetCount();
然后通过 分页方法 获取:
final List<AssetEntity> entities = await PhotoManager.getAssetListPaged(page: 0, pageCount: 80);
也通过 范围索引方法 获取:
final List<AssetEntity> entities = await PhotoManager.getAssetListRange(start: 0, end: 80);
注意: page 和start 都从 0 开始。
通过 ID 获取
ID 在不同平台代表:
- Android 平台
MediaStore的_id字段; - iOS/macOS 平台
PHAsset的localIdentifier字段。
如果你想要实现持久化选择的相关功能,你需要存储资源的 ID,
并在之后的使用中通过 AssetEntity.fromId` 来重新持有资源对象。
final AssetEntity? asset = await AssetEntity.fromId(id);
请留意资源可能访问受限,或随时被删除,所以结果可能为空。
通过原始数据获取
你可以从原始数据或文件(如下载的图像、录制的视频等)创建 AssetEntity。
创建的 AssetEntity 将储存到设备的图库中。
final Uint8List rawData = yourRawData;
// 将 `Uint8List` 保存为一张图片。
final AssetEntity? entity = await PhotoManager.editor.saveImage(
rawData,
title: 'write_your_own_title.jpg', // 可能影响 EXIF 信息的读取
);
// 通过路径来保存一张已存在的图片。
final AssetEntity? imageEntityWithPath = await PhotoManager.editor.saveImageWithPath(
path, // 使用源文件的绝对路径来保存,与复制类似。
title: 'same_as_above.jpg',
);
// 通过文件来保存视频。
final File videoFile = File('path/to/your/video.mp4');
final AssetEntity? videoEntity = await PhotoManager.editor.saveVideo(
videoFile, // 可以检查文件是否存在以获得更好的测试覆盖率。
title: 'write_your_own_title.mp4',
);
// [仅 iOS] 通过图片和视频来保存一张实况照片。
// 仅在图片和视频文件为同一张实况照片时才能生效。
final File imageFile = File('path/to/your/livephoto.heic');
final File videoFile = File('path/to/your/livevideo.mp4');
final AssetEntity? entity = await PhotoManager.editor.darwin.saveLivePhoto(
imageFile: imageFile,
videoFile: videoFile,
title: 'write_your_own_title.heic',
);
请留意资源可能访问受限,或随时被删除,所以结果可能为空。
iOS 和 macOS 可能会对资源保存附加额外的限制,目前仅部分资源类型可以保存为图库资源。
该限制由 AVFileType 定义,当你在保存时看到
PHPhotosErrorDomain Code=3302 (或者 330x) 错误时,
确保你的文件类型受支持。
通过 iCloud 获取
iOS 为了节省磁盘空间,可能将资源仅保存在 iCloud 上。
从 iCloud 检索资源文件时,速度会取决于网络状况,可能非常缓慢,使用户感到焦虑。
你可以使用 PMProgressHandler 在加载文件时提示用户当前的进度。
推荐参考的实践是 wechat_asset_picker 中的
LocallyAvailableBuilder,它会在下载文件时提供进度的展示。
有数个方法可以结合 PMProgressHandler 来提供进度反馈:
- AssetEntity.thumbnailDataWithSize
- AssetEntity.thumbnailDataWithOption
- AssetEntity.getMediaUrl
- AssetEntity.loadFile
- PhotoManager.plugin.getOriginBytes
iCloud 文件只能在设备上的 Apple ID 正常登录时获取。
当账号要求重新输入密码验证时,未缓存在本地的 iCloud 文件将无法访问,
此时相关方法会抛出 CloudPhotoLibraryErrorDomain 错误。
取消加载 (3.8.0 新增)
[!NOTE] 当前仅在 iOS 和 macOS 上可用.
上述的 AssetEntity 方法均添加了 cancelToken 参数,
可以用于取消加载过程。
其他方法如果也添加了 cancelToken 参数,同样可以用于取消加载过程。
final PMCancelToken cancelToken = PMCancelToken();
final File? file = await yourAssetEntity.loadFile(cancelToken: cancelToken);
await cancelToken.cancel();
PhotoManager 也有一个方法可以取消所有加载:
await PhotoManager.cancelAllRequest();
展示资源
从 v3.0.0 开始,插件不再提供任何 UI 组件。
AssetEntityImage 和 AssetEntityImageProvider 在
photo_manager_image_provider
插件中提供。
新的插件提供 AssetEntityImage widget 和
AssetEntityImageProvider 来处理资源的展示:
import 'package:photo_manager_image_provider/photo_manager_image_provider.dart';
final Widget image = AssetEntityImage(
yourAssetEntity,
isOriginal: false,
thumbnailSize: const ThumbnailSize.square(200),
thumbnailFormat: ThumbnailFormat.jpeg,
);
final Widget imageFromProvider = Image(
image: AssetEntityImageProvider(
yourAssetEntity,
isOriginal: false,
thumbnailSize: const ThumbnailSize.square(200),
thumbnailFormat: ThumbnailFormat.jpeg,
),
);
获取文件
AssetEntity 包含数个获取文件的 getter 和方法:
.file.fileWithSubtype.originFile.originFileWithSubtype.loadFile
这些方法会获取与该资源关联的不同类型的文件。阅读它们的文档以了解具体的用途。
另外,你可以使用 PhotoManager.plugin.getFullFile 来获取文件,该方法有完整的获取参数。
获取「实况照片」
该插件支持获取和过滤 iOS 上的实况照片。
仅过滤「实况照片」
只在过滤图片的时候支持过滤「实况照片」:
final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList(
type: RequestType.image,
filterOption: FilterOptionGroup(onlyLivePhotos: true),
);
或者你也可以使用 CustomSqlFilter 来获取「实况照片」:
final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList(
type: RequestType.image,
filterOption: CustomFilter.sql(
where: '${CustomColumns.base.mediaType} = 1'
' AND '
'${CustomColumns.darwin.mediaSubtypes} & (1 << 3) = (1 << 3)',
),
);
获取「实况照片」的视频
final AssetEntity entity = livePhotoEntity;
// 播放实况照片的视频
final String? mediaUrl = await entity.getMediaUrl();
// 获取缩略图和视频
final File? imageFile = await entity.file;
final File? videoFile = await entity.fileWithSubtype;
// 获取原图和原视频
final File? originImageFile = await entity.originFile;
final File? originVideoFile = await entity.originFileWithSubtype;
// 将实况照片的视频从 mov 转换为 mp4
final File? convertedFile = await entity.loadFile(
isOriginal: true,
withSubtye: true,
darwinFileType: PMDarwinAVFileType.mp4,
);
包含隐藏资源(仅 iOS 平台有效)
在 iOS 16 及以上版本中,用户可以要求验证才能查看隐藏相册,并且该设置默认为开启。在标准查询中,系统不会返回隐藏的资源。
本插件现在支持通过 includeHiddenAssets 选项在 iOS 平台上包含隐藏资源:
与 FilterOptionGroup 一起使用
final FilterOptionGroup filterOption = FilterOptionGroup(
// 其他选项...
includeHiddenAssets: true, // 设置为 true 以包含隐藏资源
);
// 在获取资源时使用过滤选项
final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList(
filterOption: filterOption,
);
与 CustomFilter 一起使用
final filter = CustomFilter.sql(
where: '${CustomColumns.base.width} > 100',
// 其他选项...
);
filter.includeHiddenAssets = true; // 设置为 true 以包含隐藏资源
// 在获取资源时使用过滤器
final int count = await PhotoManager.getAssetCount(filterOption: filter);
注意:此选项仅在 iOS 平台上生效。在其他平台上,它将被忽略。如果用户在 iOS 16+ 上为隐藏相册启用了验证(默认开启),即使设置了 includeHiddenAssets 为 true,第三方应用也无法访问隐藏的资源。
限制
Android 10 媒体位置权限
在 Android 10 版本中获取带有位置信息和 EXIF 元数据的原始数据时,必须授予媒体位置权限。
若需要获取,请将 ACCESS_MEDIA_LOCATION 权限添加到清单中。
原始数据的使用
originFile 和 originBytes 的 getter 会返回资源的原始数据。
然而在 Flutter 中,某些情况的原始数据是无法使用的。以下是一些常见的情况:
- 在不同平台和版本中,HEIC 文件并未被完全支持。
我们建议你上传 JPEG 文件(HEIC 图片的
.file), 以保持多个平台之间的一致行为。 查看 flutter/flutter#20522 了解更多细节。 - 视频将仅以原始格式获取,而不是组合过的格式, 这可能会在播放视频时导致某些行为的差异。
iOS 上文件检索时间过长
该插件中有几个针对 AssetEntity 的 I/O 方法:
- 所有名称带有
file的方法. AssetEntity.originBytes.
在 iOS 上,文件的检索和缓存受到沙盒机制的限制。
能获取到 PHAsset 并不意味着该资源位于设备上。
一般来说,PHAsset 会有三种状态:
isLocallyAvailable等于true, 并且已经缓存:可以获取。isLocallyAvailable等于true, 但没有缓存:当你调用 I/O 方法时,资源需要缓存在沙盒中才可以获取。isLocallyAvailable等于false,通常意味着资源仅保存在 iCloud 上,或者某些视频尚未导出过。 在这种情况下,最好使用PMProgressHandler提供响应式的用户界面。
资源变动的通知回调
插件会从原生平台广播资源变更的事件,但是在不同的平台和系统版本之间,事件携带的内容并不相同。 你可以参考 相关日志 了解各个版本和平台之间的事件日志。
要为这些事件注册回调,请使用 PhotoManager.addChangeCallback 添加回调,
并使用 PhotoManager.removeChangeCallback 移除回调,
与 addListener 和 removeListener 方法相似。
在添加/移除回调之后,你可以调用 [PhotoManager.startChangeNotify 方法启用通知,
以及 PhotoManager.stopChangeNotify 方法停止通知。
import 'package:flutter/services.dart';
void changeNotify(MethodCall call) {
// 你的自定义回调。
}
/// 注册你的回调方法。
PhotoManager.addChangeCallback(changeNotify);
/// 启用事件通知订阅。
PhotoManager.startChangeNotify();
/// 移除你的回调方法。
PhotoManager.removeChangeCallback(changeNotify);
/// 取消事件通知订阅。
PhotoManager.stopChangeNotify();
过滤资源
插件包含对资源过滤筛选的支持。
以下的方法包含 filterOption 参数,用于指定资源过滤的条件。
- PhotoManager
- getAssetPathList(可以通过
AssetPathEntity.filterOption获取) - getAssetCount
- getAssetListRange
- getAssetListPaged
- getAssetPathList(可以通过
- AssetPathEntity
- 构造(不推荐直接使用)
- fromId
- obtainPathFromProperties(不推荐直接使用)
插件支持两种形式的资源筛选:
FilterOptionGroup
FilterOptionGroup 是 2.6.0 版本前唯一支持的筛选器实现。
final FilterOptionGroup filterOption = FilterOptionGroup(
imageOption: FilterOption(
sizeConstraint: SizeConstraint(
maxWidth: 10000,
maxHeight: 10000,
minWidth: 100,
minHeight: 100,
ignoreSize: false,
),
),
videoOption: FilterOption(
durationConstraint: DurationConstraint(
min: Duration(seconds: 1),
max: Duration(seconds: 30),
allowNullable: false,
),
),
createTimeCondition: DateTimeCondition(
min: DateTime(2020, 1, 1),
max: DateTime(2020, 12, 31),
),
orders: [
OrderOption(
type: OrderOptionType.createDate,
asc: false,
),
],
/// 包含隐藏资源(仅 iOS 平台有效)
includeHiddenAssets: false,
/// 其他选项
);
CustomFilter
注意: CustomFilter 自 v2.6.0 引入。由于其存在时间较短,无法保证其稳定性。
如果在使用时遇到相关问题,请按照模板提交 issue。
CustomFilter 针对不同平台提供了更加灵活的筛选条件。
其使用方法更像平台本身的处理方法,即类 SQL 的筛选方式。
SQL 筛选的字段名称在不同平台上是不一致的,
在使用时请注意区分 CustomColumns.base、CustomColumns.android 以及 CustomColumns.darwin
来获取正确的字段名称。
构造一个 CustomFilter 的例子:
CustomFilter createFilter() {
final filter = CustomFilter.sql(
where: '${CustomColumns.base.width} > 100 AND ${CustomColumns.base.height} > 200',
orderBy: [OrderByItem.desc(CustomColumns.base.createDate)],
);
// 包含隐藏资源(仅 iOS 平台有效)
filter.includeHiddenAssets = true;
return filter;
}
更高级的 CustomFilter
AdvancedCustomFilter 继承自 CustomFilter,
可以通过 builder 方式创建一个筛选器。
CustomFilter createFilter() {
final group = WhereConditionGroup()
.and(
ColumnWhereCondition(
column: CustomColumns.base.width,
value: '100',
operator: '>',
),
)
.or(
ColumnWhereCondition(
column: CustomColumns.base.height,
value: '200',
operator: '>',
),
);
final filter = AdvancedCustomFilter()
.addWhereCondition(group)
.addOrderBy(column: CustomColumns.base.createDate, isAsc: false);
return filter;
}
相关类定义解释
CustomFilter:自定义筛选器的基类。SqlCustomFilter:类 SQL 的筛选器。AdvancedCustomFilter:构建自定义选项的筛选器。OrderByItem:实现排序的类,功能类似于 ORDER BY。WhereConditionItem:条件筛选的抽象类,功能类似于 WHERE。WhereConditionGroup:筛选条件组,将一组条件合并在同一个条件组。TextWhereCondition:字符串筛选条件,在传递时不会检查是否有效。ColumnWhereCondition:字段名筛选条件,在传递时会检查字段名是否有效。DateColumnWhereCondition:日期筛选条件,由于 iOS/macOS 上的日期格式不同,该条件可以帮助处理这些差异。
CustomColumns:不同平台的字段名。base:适用于各个平台通用的字段名,但在 iOS 上 "id" 字段无效,甚至可能会导致错误。它只在 Android 上有效。android:适用于 Android 的字段名。darwin:适用于 iOS/macOS 的字段名。
下图为自定义筛选器作用的流程图:

缓存机制
Android 缓存
由于 Android 10 限制了直接访问资源路径的能力,
因此图像缓存将在 I/O 处理过程中生成。
更具体地说,当调用 file,originFile 或任何 I/O 操作时,
插件将保存一个文件到缓存文件夹以供进一步使用。
幸运的是,在 Android 11 及以上版本中,可以再次直接获取资源路径,
在 Android 10 中,你仍然可以使用 requestLegacyExternalStorage
访问存储中的文件而不缓存它们。
有关如何添加属性,请参见 Android 10 (Q, 29)。
该属性不是必需的。
iOS 缓存
iOS 没有直接提供 API 来访问相册资源的原始文件。
因此,当调用 file、originFile 或相关的文件操作时,
将在当前应用程序的沙盒中生成对应的缓存文件。
如果在你的用例中占用磁盘空间很敏感, 那么你可以在使用完成后删除它(仅适用于 iOS)。
import 'dart:io';
Future<void> useEntity(AssetEntity entity) async {
File? file;
try {
file = await entity.file;
await handleFile(file!); // 处理获取的文件
} finally {
if (Platform.isIOS) {
file?.deleteSync(); // 处理完成后删除
}
}
}
清除缓存
你可以使用 PhotoManager.clearFileCache 方法来清除插件生成的所有缓存。
缓存的生成取决于不同平台、类型和分辨率等情况。
| 平台 | 缩略图 | 文件 / 原始文件 |
|---|---|---|
| Android | 生成 | 生成 (Android 10+) |
| iOS | 不生成 | 生成 |
原生额外配置
Android 额外配置
Glide 相关问题
如果你的项目存在 Glide 的版本冲突问题,
那么你需要编辑 android/build.gradle 文件:
rootProject.allprojects {
subprojects {
project.configurations.all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == 'com.github.bumptech.glide'
&& details.requested.name.contains('glide')) {
details.useVersion '4.14.2'
}
}
}
}
}
如果你想了解如何同时使用 ProGuard 和 Glide,请参阅 ProGuard for Glide。
Android 14 (API level 34) 额外配置
当你的应用在 API 34 (Android 14) 的设备上运行时,
就算你的 targetSdkVersion 和 compileSdkVersion 不是 34,
你也需要在清单文件中添加以下权限配置:
<manifest>
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" /> <!-- 如果需要提供可选的资源的功能 -->
</manifest>
Android 13 (API level 33) 额外配置
当你的应用在 API 33 (Android 13) 的设备上运行时,
就算你的 targetSdkVersion 和 compileSdkVersion 不是 33,
你也需要在清单文件中添加以下权限配置:
注意:
READ_MEDIA_IMAGES和READ_MEDIA_VIDEO无论在请求图片还是请求视频时都需要。
<manifest>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 如果需要读取图片或视频 -->
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- 如果需要读取视频或图片 -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- 如果需要读取音频 -->
</manifest>
Android 12 (API 31) MANAGE_MEDIA (可选)
Android 12 引入了 MANAGE_MEDIA 这个特殊权限。授予该权限后,
PhotoManager.editor.deleteWithIds、moveToTrash、收藏等操作不再触发系统的
逐次确认弹窗,适合有批量操作诉求的相册 / 媒体管理类应用。
如果需要启用,请在 AndroidManifest.xml 中声明:
<manifest>
<uses-permission android:name="android.permission.MANAGE_MEDIA" />
</manifest>
用户还需要在 设置 > 应用 > 特殊应用权限 > 媒体管理 里手动为你的应用打开开关,
系统不会弹出运行时弹窗。可以使用下列 API 检查状态并跳转到设置页:
if (!await PhotoManager.canManageMedia()) {
await PhotoManager.requestManageMedia();
// 用户从设置页返回后再次调用 canManageMedia() 检查是否已授予。
}
两个方法在 iOS / macOS / OpenHarmony 及 Android 12 以下均返回 false。
READ_MEDIA_* 和 ACCESS_MEDIA_LOCATION 仍然是前置依赖,
MANAGE_MEDIA 只是移除了系统的确认弹窗。
iOS 额外配置
配置系统相册名称的国际化
默认情况下,无论设备上设置了什么语言,iOS 都只会以英语检索系统相册的名称。 要更改默认语言,请按照以下步骤操作:
用 Xcode 打开你的 iOS 项目 (Runner.xcworkspace)

选择你项目的 “Runner”,在本地化表格中单击加号图标。

选择你想要检索本地化的语言。
在不进行任何修改的情况下验证弹出屏幕。
重新构建你的 Flutter 项目。
现在系统相册的名称应该能够以对应的语言显示。
注意: 本地化相册名称不代表自定义。
实验性功能
警告: 此处的功能不能保证在所有平台和系统版本下完全可用, 因为它们涉及到数据修改。 它们可能在任何版本中随时被修改或删除。
某些 API 将对数据进行不可逆的修改和删除。 在使用这些功能时,请谨慎操作,并最好实现先行测试。
预加载缩略图
你可以使用 PhotoCachingManager.requestCacheAssets
或 PhotoCachingManager.requestCacheAssetsWithIds 方法
以特定的缩略图选项来加载部分资源的缩略图。
PhotoCachingManager().requestCacheAssets(assets: assets, option: option);
你也可以通过调用
PhotoCachingManager().cancelCacheRequest
方法随时停止预加载。
通常在 app 预览资源时,会使用缩略图进行展示。 但有时我们希望预加载资源以使其显示更快。
PhotoCachingManager 在 iOS 上使用的是 PHCachingImageManager,
在 Android 上使用 Glide 的文件缓存。
删除资源
此方法将从你的图库中完全删除资源,请谨慎使用。
// 调用方法会返回被删除的资源,如果全部失败会返回空列表。
// 不存在的资源 ID 会被忽略。
final List<String> result = await PhotoManager.editor.deleteWithIds(
<String>[entity.id],
);
删除后,你可以调用 refreshPathProperties 方法刷新相应的
AssetPathEntity 以便更新字段。
复制资源
你可以使用 copyAssetToPath 方法将资源 “复制” 到目标 AssetPathEntity 中:
// 确保 anotherPathEntity 对于当前 app 而言可以访问。
final AssetPathEntity anotherPathEntity = anotherAccessiblePath;
final AssetEntity entity = yourEntity;
final AssetEntity? newEntity = await PhotoManager.editor.copyAssetToPath(
asset: entity,
pathEntity: anotherPathEntity,
); // 如果 anotherPathEntity 无法访问,结果会返回 null。
“复制” 在 Android 和 iOS 上有不同的含义:
- 对于 Android,它会插入源资源的副本:
- 在 SDK <= 28 上,该方法将复制大部分来源信息。
- 在 SDK >= 29 上,某些字段无法在插入期间修改,如 MediaColumns.RELATIVE_PATH.
- 对于 iOS,它会创建一个快捷方式,而不是创建一个新的资源。
- 某些相册是智能相册,它们的内容由系统自动管理,不能手动插入资源。
(对于 Android 30+,由于系统限制,此功能当前被屏蔽。)
仅适用于 Android 的功能
将资源移动到另一个相册
// 确保 accessiblePath 对于当前 app 而言可以访问。
final AssetPathEntity pathEntity = accessiblePath;
final AssetEntity entity = yourEntity;
await PhotoManager.editor.android.moveAssetToAnother(
entity: entity,
target: pathEntity,
);
(对于 Android 30+,由于系统限制,此功能当前被屏蔽。)
将资源移动到废纸篓
await PhotoManager.editor.android.moveToTrash(list);
这个方法用于将资源移动到废纸篓,它仅支持安卓 API 30+,低于 30 的 API 会抛出异常。
移除所有不存在的资源
这将删除所有本地不存在的相册条目。
安卓的 MediaStore 中的记录对应的文件可能会被其他的 app 或文件管理器删除。
这些异常行为通常是由文件管理器、辅助工具或 adb 工具造成的。
此操作很消耗资源,请不要重复调用。
await PhotoManager.editor.android.removeAllNoExistsAsset();
某些系统会在每个资源删除时分别弹出确认对话框,这是无法避免的。 请确认你需要调用该方法,并且你的客户接受反复弹窗确认。
适用于 iOS 或 macOS 的功能
创建一个文件夹
PhotoManager.editor.darwin.createFolder(
name,
parent: parent, // 应为 null、根目录或者其他可访问的文件夹
);
创建一个相簿
PhotoManager.editor.darwin.createAlbum(
name,
parent: parent, // 应为 null、根目录或者其他可访问的文件夹
);
从相册中移除资源
从特定相册中移除资源。 该移除不会从设备中删除,只会从相册中被移除。
// 确保你的路径能够访问。
final AssetPathEntity pathEntity = accessiblePath;
final AssetEntity entity = yourEntity;
final List<AssetEntity> entities = <AssetEntity>[yourEntity, anotherEntity];
// 移除相簿的单个图片
// 这将调用列表移除的方法作为实现。
await PhotoManager.editor.darwin.removeInAlbum(
yourEntity,
accessiblePath,
);
// 批量从相册中移除资源。
await PhotoManager.editor.darwin.removeAssetsInAlbum(
entities,
accessiblePath,
);
删除 AssetPathEntity
智能相册无法被删除。
PhotoManager.editor.darwin.deletePath();
Darwin 专属只读接口(AssetEntity.darwin / AssetPathEntity.darwin)
PhotoKit 专属的只读能力统一收拢在 .darwin 访问器下,避免让公共实体类膨胀。
在非 Darwin 平台访问 .darwin 会抛出 OSError,请用 Platform 判断后再调用
(或仅在 iOS/macOS 上使用)。
// 云标识在同一 iCloud 图库的多设备间保持稳定(iOS 15+/macOS 12+),
// 不像每台设备各不相同的 `AssetEntity.id`。
final String? cloudId = await entity.darwin.cloudIdentifier;
// 批量解析多个资源时优先用批量接口,比在循环里逐个读取高效得多,
// 返回以 localIdentifier 为键的 map。
final Map<String, String?> cloudIds =
await PhotoManager.plugin.getCloudIdentifiers(<String>[entity.id, ...]);
// 判断资源是否有编辑,以及导出未编辑的原始文件。
if (await entity.darwin.hasAdjustments) {
final File? base = await entity.darwin.baseFile();
}
// 获取包含某相册/文件夹的父级文件夹,可用于面包屑导航。
final List<AssetPathEntity> parents = await path.darwin.getParentPathList();
修改资源的创建日期
修改已有资源的创建日期。成功返回更新后的 AssetEntity,失败抛出 StateError。
- iOS/macOS:更新
PHAsset.creationDate。 - Android Q(API 29)及以上:更新 MediaStore 的
DATE_TAKEN字段 (API 28 及以下不支持)。
final AssetEntity updated =
await PhotoManager.editor.darwin.updateCreationDate( // 或 .android
entity: asset,
creationDate: DateTime(2023, 1, 15, 10, 30),
);
适用于 OpenHarmony 的功能
鸿蒙官方处于安全考虑已禁止相关资源能力。
目前以支持除缓存相关的其他功能,且只支持图片和视频类型资源。
| Feature | OpenHarmony |
|---|---|
| releaseCache | ❌ |
| clearFileCache | ❌ |
| requestCacheAssetsThumbnail | ❌ |
| getSubPathEntities | ❌ |