CINXE.COM
photo_manager | Flutter package
<!DOCTYPE html> <html lang="en-us"><head><script src="https://www.googletagmanager.com/gtm.js?id=GTM-MX6DBN9" async="async"></script><script src="/static/hash-o6oemknr/js/gtm.js" async="async"></script><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="ie=edge"/><meta name="viewport" content="width=device-width, initial-scale=1"/><meta name="twitter:card" content="summary"/><meta name="twitter:site" content="@dart_lang"/><meta name="twitter:description" content="A Flutter plugin that provides album assets abstraction management APIs on Android, iOS, macOS, and OpenHarmony."/><meta name="twitter:image" content="https://pub.dev/static/hash-o6oemknr/img/pub-dev-icon-cover-image.png"/><meta property="og:type" content="website"/><meta property="og:site_name" content="Dart packages"/><meta property="og:title" content="photo_manager | Flutter package"/><meta property="og:description" content="A Flutter plugin that provides album assets abstraction management APIs on Android, iOS, macOS, and OpenHarmony."/><meta property="og:image" content="https://pub.dev/static/hash-o6oemknr/img/pub-dev-icon-cover-image.png"/><meta property="og:url" content="https://pub.dev/packages/photo_manager"/><title>photo_manager | Flutter package</title><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&family=Google+Sans+Display:wght@400&family=Google+Sans+Text:wght@400;500;700&family=Google+Sans+Mono:wght@400;700&display=swap"/><link rel="shortcut icon" href="/static/hash-o6oemknr/img/flutter-logo-32x32.png"/><link rel="stylesheet" href="https://www.gstatic.com/glue/v25_0/ccb.min.css"/><link rel="search" type="application/opensearchdescription+xml" title="Dart packages" href="/osd.xml"/><link rel="canonical" href="https://pub.dev/packages/photo_manager"/><meta name="description" content="A Flutter plugin that provides album assets abstraction management APIs on Android, iOS, macOS, and OpenHarmony."/><link rel="alternate" type="application/atom+xml" title="Updated Packages Feed for Pub" href="/feed.atom"/><link rel="stylesheet" type="text/css" href="/static/hash-o6oemknr/material/bundle/styles.css"/><link rel="stylesheet" type="text/css" href="/static/hash-o6oemknr/css/style.css"/><script src="/static/hash-o6oemknr/material/bundle/script.min.js" defer="defer"></script><script src="/static/hash-o6oemknr/js/script.dart.js" defer="defer"></script><script src="https://www.gstatic.com/brandstudio/kato/cookie_choice_component/cookie_consent_bar.v3.js" defer="defer" data-autoload-cookie-consent-bar="true"></script><meta name="pub-page-data" content="eyJwa2dEYXRhIjp7InBhY2thZ2UiOiJwaG90b19tYW5hZ2VyIiwidmVyc2lvbiI6IjMuNi4zIiwibGlrZXMiOjY3OCwicHVibGlzaGVySWQiOiJmbHV0dGVyY2FuZGllcy5jb20iLCJpc0Rpc2NvbnRpbnVlZCI6ZmFsc2UsImlzTGF0ZXN0Ijp0cnVlfSwic2Vzc2lvbkF3YXJlIjpmYWxzZX0="/><link rel="preload" href="/static/hash-o6oemknr/highlight/highlight-with-init.js" as="script"/></head><body class="light-theme"><script src="/static/hash-o6oemknr/js/dark-init.js"></script><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-MX6DBN9" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript><div class="site-header"><button class="hamburger" aria-label="menu toggle"></button><a class="logo" href="/"><img class="site-logo" src="/static/hash-o6oemknr/img/pub-dev-logo.svg" alt="" width="140" height="30" role="presentation"/></a><div class="site-header-space"></div><div class="site-header-mask"></div><div class="site-header-search"><form action="/packages" method="GET"><input class="site-header-search-input" name="q" placeholder="New search..." autocomplete="on" title="Search"/></form></div><nav class="site-header-nav scroll-container"><div class="nav-login-container"><button id="-account-login" class="nav-main-button link">Sign in</button></div><div class="nav-container nav-help-container hoverable"><button class="nav-main-button">Help</button><div class="nav-hover-popup"><div class="nav-table-columns"><div class="nav-table-column"><h3>Pub.dev</h3><a class="nav-link" href="/help/search" rel="noopener" target="_blank">Searching for packages</a><a class="nav-link" href="/help/scoring" rel="noopener" target="_blank">Package scoring and pub points</a></div><div class="nav-table-column"><h3>Flutter</h3><a class="nav-link" href="https://flutter.dev/using-packages/" rel="noopener" target="_blank">Using packages</a><a class="nav-link" href="https://flutter.dev/developing-packages/" rel="noopener" target="_blank">Developing packages and plugins</a><a class="nav-link" href="https://dart.dev/tools/pub/publishing" rel="noopener" target="_blank">Publishing a package</a></div><div class="nav-table-column"><h3>Dart</h3><a class="nav-link" href="https://dart.dev/guides/packages" rel="noopener" target="_blank">Using packages</a><a class="nav-link" href="https://dart.dev/tools/pub/publishing" rel="noopener" target="_blank">Publishing a package</a></div></div></div></div><div class="nav-container nav-help-container-mobile foldable"><h3 class="foldable-button">Pub.dev <img class="foldable-icon" src="/static/hash-o6oemknr/img/nav-mobile-foldable-icon.svg" alt="toggle folding of the section" width="13" height="6"/></h3><div class="foldable-content"><a class="nav-link" href="/help/search" rel="noopener" target="_blank">Searching for packages</a><a class="nav-link" href="/help/scoring" rel="noopener" target="_blank">Package scoring and pub points</a></div></div><div class="nav-container nav-help-container-mobile foldable"><h3 class="foldable-button">Flutter <img class="foldable-icon" src="/static/hash-o6oemknr/img/nav-mobile-foldable-icon.svg" alt="toggle folding of the section" width="13" height="6"/></h3><div class="foldable-content"><a class="nav-link" href="https://flutter.dev/using-packages/" rel="noopener" target="_blank">Using packages</a><a class="nav-link" href="https://flutter.dev/developing-packages/" rel="noopener" target="_blank">Developing packages and plugins</a><a class="nav-link" href="https://dart.dev/tools/pub/publishing" rel="noopener" target="_blank">Publishing a package</a></div></div><div class="nav-container nav-help-container-mobile foldable"><h3 class="foldable-button">Dart <img class="foldable-icon" src="/static/hash-o6oemknr/img/nav-mobile-foldable-icon.svg" alt="toggle folding of the section" width="13" height="6"/></h3><div class="foldable-content"><a class="nav-link" href="https://dart.dev/guides/packages" rel="noopener" target="_blank">Using packages</a><a class="nav-link" href="https://dart.dev/tools/pub/publishing" rel="noopener" target="_blank">Publishing a package</a></div></div></nav></div><div id="banner-container"></div><main class="container"><div class="detail-wrapper -active -has-info-box"><div class="detail-header -is-loose"><div class="detail-container"><div class="detail-header-outer-block"><div class="detail-header-content-block"><h1 class="title">photo_manager 3.6.3 <span class="pkg-page-title-copy"><img class="pkg-page-title-copy-icon filter-invert-on-dark" src="/static/hash-o6oemknr/img/content-copy-icon.svg" alt="copy "photo_manager: ^3.6.3" to clipboard" width="18" height="18" title="Copy "photo_manager: ^3.6.3" to clipboard" data-copy-content="photo_manager: ^3.6.3" data-ga-click-event="copy-package-version"/><div class="pkg-page-title-copy-feedback"><span class="code">photo_manager: ^3.6.3</span> copied to clipboard</div></span></h1><div class="metadata">Published <span><a class="-x-ago" href="" title="Nov 21, 2024" aria-label="9 days ago" aria-role="button" role="button" data-timestamp="1732188850822">9 days ago</a></span> • <a class="-pub-publisher" href="/publishers/fluttercandies.com"><img class="-pub-publisher-shield filter-invert-on-dark" src="/static/hash-o6oemknr/img/material-icon-verified.svg" alt="verified publisher" width="14" height="14" title="Published by a pub.dev verified publisher"/>fluttercandies.com</a><span class="package-badge" title="Package is compatible with Dart 3.">Dart 3 compatible</span></div><div class="detail-tags-and-like"><div class="detail-tags"><div class="-pub-tag-badge"><span class="tag-badge-main">SDK</span><a class="tag-badge-sub" href="/packages?q=sdk%3Aflutter" rel="nofollow" title="Packages compatible with Flutter SDK">Flutter</a></div><div class="-pub-tag-badge"><span class="tag-badge-main">Platform</span><a class="tag-badge-sub" href="/packages?q=platform%3Aandroid" rel="nofollow" title="Packages compatible with Android platform">Android</a><a class="tag-badge-sub" href="/packages?q=platform%3Aios" rel="nofollow" title="Packages compatible with iOS platform">iOS</a><a class="tag-badge-sub" href="/packages?q=platform%3Amacos" rel="nofollow" title="Packages compatible with macOS platform">macOS</a></div></div><div class="detail-like"><button id="-pub-like-icon-button" class="mdc-icon-button" data-ga-click-event="toggle-like" aria-pressed="false" title="Like this package"><img class="mdc-icon-button__icon" src="/static/hash-o6oemknr/img/like-inactive.svg" alt="liked status: inactive" width="18" height="18"/><img class="mdc-icon-button__icon mdc-icon-button__icon--on" src="/static/hash-o6oemknr/img/like-active.svg" alt="liked status: active" width="18" height="18"/></button><span class="likes-count"><span id="likes-count">678</span></span></div></div></div></div></div></div><div class="detail-container"><div class="detail-lead"><div class="detail-metadata-toggle"><div class="detail-metadata-toggle-icon">→</div><h3 class="detail-lead-title">Metadata</h3></div><p class="detail-lead-text">A Flutter plugin that provides album assets abstraction management APIs on Android, iOS, macOS, and OpenHarmony.</p><p class="detail-lead-more"><a class="detail-metadata-toggle">More...</a></p></div></div><div class="detail-body"><div class="detail-tabs"><div class="detail-tabs-wide-header"><div class="detail-container"><ul class="detail-tabs-header"><li class="detail-tab tab-button detail-tab-readme-title -active">Readme</li><li class="detail-tab tab-link detail-tab-changelog-title"><a href="/packages/photo_manager/changelog" role="button">Changelog</a></li><li class="detail-tab tab-link detail-tab-example-title"><a href="/packages/photo_manager/example" role="button">Example</a></li><li class="detail-tab tab-link detail-tab-installing-title"><a href="/packages/photo_manager/install" role="button">Installing</a></li><li class="detail-tab tab-link detail-tab-versions-title"><a href="/packages/photo_manager/versions" role="button">Versions</a></li><li class="detail-tab tab-link detail-tab-analysis-title"><a href="/packages/photo_manager/score" role="button">Scores</a></li></ul></div></div><div class="detail-container detail-body-main"><div class="detail-tabs-content"><section class="tab-content detail-tab-readme-content -active markdown-body"><!-- Copyright 2018 The FlutterCandies author. All rights reserved. Use of this source code is governed by an Apache license that can be found in the LICENSE file. --> <h1 class="hash-header" id="photo_manager">photo_manager <a href="#photo_manager" class="hash-link">#</a></h1> <p>English | <a href="https://github.com/fluttercandies/flutter_photo_manager/blob/main/README-ZH.md" rel="ugc">中文</a></p> <p><a href="https://pub.dev/packages/photo_manager"><img src="https://img.shields.io/pub/v/photo_manager?label=stable" alt="pub package"></a> <a href="https://pub.dev/packages/photo_manager"><img src="https://img.shields.io/pub/v/photo_manager?color=9d00ff&include_prereleases&label=dev" alt="pub pre-release package"></a> <a href="https://github.com/fluttercandies/flutter_photo_manager/actions/workflows/runnable.yml" rel="ugc"><img src="https://img.shields.io/github/actions/workflow/status/fluttercandies/flutter_photo_manager/runnable.yml?branch=main&label=CI&logo=github&style=flat-square" alt="Build status"></a> <a href="https://github.com/fluttercandies/flutter_photo_manager/blob/main/LICENSE" rel="ugc"><img src="https://img.shields.io/github/license/fluttercandies/flutter_photo_manager" alt="GitHub license"></a></p> <p><a href="https://github.com/fluttercandies/flutter_photo_manager/stargazers" rel="ugc"><img src="https://img.shields.io/github/stars/fluttercandies/flutter_photo_manager?logo=github&style=flat-square" alt="GitHub stars"></a> <a href="https://github.com/fluttercandies/flutter_photo_manager/network" rel="ugc"><img src="https://img.shields.io/github/forks/fluttercandies/flutter_photo_manager?logo=github&style=flat-square" alt="GitHub forks"></a> <a href="https://github.com/Solido/awesome-flutter" rel="ugc"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome Flutter"></a> <a target="_blank" href="https://jq.qq.com/?_wv=1027&k=5bcc0gy" rel="ugc"><img border="0" src="https://pub.idqqimg.com/wpa/images/group.png" alt="FlutterCandies" title="FlutterCandies"></a></p> <p>A Flutter plugin that provides assets abstraction management APIs without UI integration, you can get assets (image/video/audio) on Android, iOS, macOS and OpenHarmony.</p> <h2 class="hash-header" id="projects-using-this-plugin">Projects using this plugin <a href="#projects-using-this-plugin" class="hash-link">#</a></h2> <table> <thead> <tr> <th align="left">name</th> <th align="left">pub</th> <th align="left">github</th> </tr> </thead> <tbody> <tr> <td align="left">wechat_assets_picker</td> <td align="left"><a href="https://pub.dev/packages/wechat_assets_picker"><img src="https://img.shields.io/pub/v/wechat_assets_picker" alt="pub package"></a></td> <td align="left"><a href="https://github.com/fluttercandies/flutter_wechat_assets_picker" rel="ugc"><img src="https://img.shields.io/github/stars/fluttercandies/flutter_wechat_assets_picker?style=social" alt="star"></a></td> </tr> <tr> <td align="left">wechat_camera_picker</td> <td align="left"><a href="https://pub.dev/packages/wechat_camera_picker"><img src="https://img.shields.io/pub/v/wechat_camera_picker" alt="pub package"></a></td> <td align="left"><a href="https://github.com/fluttercandies/flutter_wechat_camera_picker" rel="ugc"><img src="https://img.shields.io/github/stars/fluttercandies/flutter_wechat_camera_picker?style=social" alt="star"></a></td> </tr> </tbody> </table> <h2 class="hash-header" id="articles-about-this-plugin">Articles about this plugin <a href="#articles-about-this-plugin" class="hash-link">#</a></h2> <ul> <li><a href="https://medium.flutter.cn/hard-to-manage-media-with-flutter-try-photo-manager-the-all-in-one-solution-5188599e4cf" rel="ugc">Hard to manage media with Flutter? Try photo_manager, the all-in-one solution</a></li> </ul> <h2 class="hash-header" id="migration-guide">Migration guide <a href="#migration-guide" class="hash-link">#</a></h2> <p>For versions upgrade across major versions, see the <a href="https://github.com/fluttercandies/flutter_photo_manager/blob/main/MIGRATION_GUIDE.md" rel="ugc">migration guide</a> for detailed info.</p> <details> <summary>TOC</summary> <!-- TOC --> <ul> <li><a href="#photo_manager">photo_manager</a> <ul> <li><a href="#projects-using-this-plugin">Projects using this plugin</a></li> <li><a href="#articles-about-this-plugin">Articles about this plugin</a></li> <li><a href="#migration-guide">Migration guide</a></li> <li><a href="#common-issues">Common issues</a></li> <li><a href="#prepare-for-use">Prepare for use</a> <ul> <li><a href="#add-the-plugin-reference-to-pubspecyaml">Add the plugin reference to pubspec.yaml</a></li> <li><a href="#import-in-your-projects">Import in your projects</a></li> <li><a href="#configure-native-platforms">Configure native platforms</a> <ul> <li><a href="#android-config-preparation">Android config preparation</a> <ul> <li><a href="#kotlin-gradle-agp">Kotlin, Gradle, AGP</a></li> <li><a href="#android-10-q-29">Android 10 (Q, 29)</a></li> <li><a href="#glide">Glide</a></li> </ul> </li> <li><a href="#ios-config-preparation">iOS config preparation</a></li> </ul> </li> </ul> </li> <li><a href="#usage">Usage</a> <ul> <li><a href="#request-for-permission">Request for permission</a> <ul> <li><a href="#limited-entities-access">Limited entities access</a> <ul> <li><a href="#limited-entities-access-on-ios">Limited entities access on iOS</a></li> <li><a href="#limited-entities-access-on-android">Limited entities access on Android</a></li> </ul> </li> </ul> </li> <li><a href="#get-albumsfolders-assetpathentity">Get albums/folders (<code>AssetPathEntity</code>)</a> <ul> <li><a href="#params-of-getassetpathlist">Params of <code>getAssetPathList</code></a></li> <li><a href="#pmpathfilteroption">PMPathFilterOption</a></li> </ul> </li> <li><a href="#get-assets-assetentity">Get assets (<code>AssetEntity</code>)</a> <ul> <li><a href="#from-assetpathentity">From <code>AssetPathEntity</code></a></li> <li><a href="#from-photomanager-since-26">From <code>PhotoManager</code> (Since 2.6)</a></li> <li><a href="#from-id">From ID</a></li> <li><a href="#from-raw-data">From raw data</a></li> <li><a href="#from-icloud">From iCloud</a></li> <li><a href="#display-assets">Display assets</a></li> <li><a href="#get-asset-files">Get asset files</a></li> <li><a href="#obtain-live-photos">Obtain "Live Photos"</a> <ul> <li><a href="#filtering-only-live-photos">Filtering only "Live Photos"</a></li> <li><a href="#obtain-the-video-from-live-photos">Obtain the video from "Live Photos"</a></li> </ul> </li> <li><a href="#limitations">Limitations</a> <ul> <li><a href="#android-10-media-location-permission">Android 10 media location permission</a></li> <li><a href="#usage-of-the-original-data">Usage of the original data</a></li> <li><a href="#long-retrieving-duration-with-file-on-ios">Long retrieving duration with file on iOS</a></li> </ul> </li> </ul> </li> <li><a href="#entities-change-notify">Entities change notify</a></li> </ul> </li> <li><a href="#filtering">Filtering</a> <ul> <li><a href="#filteroptiongroup">FilterOptionGroup</a></li> <li><a href="#customfilter">CustomFilter</a> <ul> <li><a href="#advanced-customfilter">Advanced CustomFilter</a></li> <li><a href="#classes-explanations">Classes explanations</a></li> </ul> </li> </ul> </li> <li><a href="#cache-mechanism">Cache mechanism</a> <ul> <li><a href="#cache-on-android">Cache on Android</a></li> <li><a href="#cache-on-ios">Cache on iOS</a></li> <li><a href="#clear-caches">Clear caches</a></li> </ul> </li> <li><a href="#native-extra-configs">Native extra configs</a> <ul> <li><a href="#android-extra-configs">Android extra configs</a> <ul> <li><a href="#glide-issues">Glide issues</a></li> <li><a href="#android-14-api-34-extra-configs">Android 14 (API 34) extra configs</a></li> <li><a href="#android-13-api-33-extra-configs">Android 13 (API 33) extra configs</a></li> </ul> </li> <li><a href="#ios-extra-configs">iOS extra configs</a> <ul> <li><a href="#localized-system-albums-name">Localized system albums name</a></li> </ul> </li> <li><a href="#experimental-features">Experimental features</a> <ul> <li><a href="#preload-thumbnails">Preload thumbnails</a></li> <li><a href="#delete-entities">Delete entities</a></li> <li><a href="#copy-an-entity">Copy an entity</a></li> <li><a href="#features-for-android-only">Features for Android only</a> <ul> <li><a href="#move-an-entity-to-another-album">Move an entity to another album</a></li> <li><a href="#remove-all-non-exist-entities">Remove all non-exist entities</a></li> <li><a href="#move-entities-to-trash">Move entities to trash</a></li> </ul> </li> <li><a href="#features-for-ios-or-macos">Features for iOS or macOS</a> <ul> <li><a href="#create-a-folder">Create a folder</a></li> <li><a href="#create-an-album">Create an album</a></li> <li><a href="#remove-the-entity-entry-from-the-album">Remove the entity entry from the album</a></li> <li><a href="#delete-assetpathentity">Delete <code>AssetPathEntity</code></a></li> </ul> </li> <li><a href="#features-for-openharmony">Features for OpenHarmony</a></li> </ul> </li> </ul> </li> </ul> </li> </ul> <!-- TOC --> </details> <h2 class="hash-header" id="common-issues">Common issues <a href="#common-issues" class="hash-link">#</a></h2> <p>Please search common issues in <a href="https://github.com/fluttercandies/flutter_photo_manager/issues" rel="ugc">GitHub issues</a> for build errors, runtime exceptions, etc.</p> <h2 class="hash-header" id="prepare-for-use">Prepare for use <a href="#prepare-for-use" class="hash-link">#</a></h2> <h3 class="hash-header" id="add-the-plugin-reference-to-pubspecyaml">Add the plugin reference to pubspec.yaml <a href="#add-the-plugin-reference-to-pubspecyaml" class="hash-link">#</a></h3> <p>Two ways to add the plugin to your pubspec:</p> <ul> <li><strong>(Recommend)</strong> Run <code>flutter pub add photo_manager</code>.</li> <li>Add the plugin reference in your <code>pubspec.yaml</code>'s <code>dependencies</code> section:</li> </ul> <pre><code class="language-yaml">dependencies: photo_manager: $latest_version </code></pre> <p>The latest stable version is: <a href="https://pub.dev/packages/photo_manager"><img src="https://img.shields.io/pub/v/photo_manager.svg" alt="pub package"></a></p> <h3 class="hash-header" id="import-in-your-projects">Import in your projects <a href="#import-in-your-projects" class="hash-link">#</a></h3> <pre><code class="language-dart">import 'package:photo_manager/photo_manager.dart'; </code></pre> <h3 class="hash-header" id="configure-native-platforms">Configure native platforms <a href="#configure-native-platforms" class="hash-link">#</a></h3> <p>Minimum platform versions: <strong>Android 16, iOS 9.0, macOS 10.15</strong>.</p> <ul> <li>Android: <a href="#android-config-preparation">Android config preparation</a>.</li> <li>iOS: <a href="#ios-config-preparation">iOS config preparation</a>.</li> <li>macOS: Pretty much the same with iOS.</li> </ul> <h4 id="android-config-preparation">Android config preparation</h4> <h5 id="kotlin-gradle-agp">Kotlin, Gradle, AGP</h5> <p>We ship this plugin with <strong>Kotlin <code>1.7.22</code></strong>. If your projects use a lower version of Kotlin/Gradle/AGP, please upgrade them to a newer version.</p> <p>More specifically:</p> <ul> <li>Upgrade your Gradle version (<code>gradle-wrapper.properties</code>) to <code>7.5.1</code> or the latest version.</li> <li>Upgrade your Kotlin version (<code>ext.kotlin_version</code>) to <code>1.7.22</code> or the latest version.</li> <li>Upgrade your AGP version (<code>com.android.tools.build:gradle</code>) to <code>7.2.2</code> or the latest version.</li> </ul> <h5 id="android-10-q-29">Android 10 (Q, 29)</h5> <p><em>If you're not setting your <code>compileSdkVersion</code> or <code>targetSdkVersion</code> to 29, you can skip this section.</em></p> <p>On Android 10, <strong>Scoped Storage</strong> was introduced, which causes the origin resource file not directly inaccessible through it file path.</p> <p>If your <code>compileSdkVersion</code> or <code>targetSdkVersion</code> is <code>29</code>, you can consider adding <code>android:requestLegacyExternalStorage="true"</code> to your <code>AndroidManifest.xml</code> in order to obtain resources:</p> <pre><code class="language-xml"><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> </code></pre> <p><strong>Note: Apps that are using the flag will be rejected from the Google Play.</strong></p> <p>This is not a requirement, the plugin can still work with caching files. But you'll need to control caches on your own, the best practice is to clear file caches each time when you start your app by calling <code>PhotoManager.clearFileCache()</code>.</p> <h5 id="glide">Glide</h5> <p>The plugin use <a href="https://bumptech.github.io/glide/" rel="ugc">Glide</a> to create thumbnail bytes for Android.</p> <p>If you found some warning logs with Glide appearing, it means the main project needs an implementation of <code>AppGlideModule</code>. See <a href="https://bumptech.github.io/glide/doc/generatedapi.html" rel="ugc">Generated API</a> for the implementation.</p> <h4 id="ios-config-preparation">iOS config preparation</h4> <p>Define the <code>NSPhotoLibraryUsageDescription</code> key-value in the <code>ios/Runner/Info.plist</code>:</p> <pre><code class="language-plist"><key>NSPhotoLibraryUsageDescription</key> <string>In order to access your photo library</string> </code></pre> <p>If you want to grant only write-access to the photo library on iOS 11 and above, define the <code>NSPhotoLibraryAddUsageDescription</code> key-value in the <code>ios/Runner/Info.plist</code>. It's pretty much the same as the <code>NSPhotoLibraryUsageDescription</code>.</p> <p><img src="https://raw.githubusercontent.com/CaiJingLong/some_asset/master/flutter_photo2.png" alt="permissions in Xcode"></p> <h2 class="hash-header" id="usage">Usage <a href="#usage" class="hash-link">#</a></h2> <h3 class="hash-header" id="request-for-permission">Request for permission <a href="#request-for-permission" class="hash-link">#</a></h3> <p>Most of the APIs can only use with granted permission.</p> <pre><code class="language-dart">final PermissionState ps = await PhotoManager.requestPermissionExtend(); // the method can use optional param `permission`. if (ps.isAuth) { // Granted // You can to get assets here. } else if (ps.hasAccess) { // Access will continue, but the amount visible depends on the user's selection. } else { // Limited(iOS) or Rejected, use `==` for more precise judgements. // You can call `PhotoManager.openSetting()` to open settings for further steps. } </code></pre> <p>But if you're pretty sure your callers will be only called after the permission is granted, you can ignore permission checks:</p> <pre><code class="language-dart">PhotoManager.setIgnorePermissionCheck(true); </code></pre> <p>For background processing (such as when the app is not in the foreground), ignore permissions check would be proper solution.</p> <p>You can also read the current permission state with <code>PhotoManager.getPermissionState</code>. Make sure the same permission request option is used between this request and other asset requests.</p> <h4 id="limited-entities-access">Limited entities access</h4> <h5 id="limited-entities-access-on-ios">Limited entities access on iOS</h5> <p>With iOS 14 released, Apple brought a "Limited Photos Library" permission (<code>PermissionState.limited</code>) to iOS. The <code>PhotoManager.requestPermissionExtend()</code> method will return <code>PermissionState</code>. See <a href="https://developer.apple.com/documentation/photokit/phauthorizationstatus?language=objc" rel="ugc">PHAuthorizationStatus</a> for more detail.</p> <p>To reselect accessible entities for the app, use <code>PhotoManager.presentLimited()</code> to call the modal of accessible entities' management. This method only available for iOS 14+ and when the permission state is limited (<code>PermissionState.limited</code>).</p> <p>To suppress the automatic prompting from the system when each time you access the media after the app has restarted, you can set the <code>Prevent limited photos access alert</code> key to <code>YES</code> in your app's <code>Info.plist</code> (or manually writing as below):</p> <pre><code class="language-plist"><key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key> <true/> </code></pre> <h5 id="limited-entities-access-on-android">Limited entities access on Android</h5> <p>Android 14 (API 34) has also introduced the concept of limited assets similar to iOS.</p> <p>However, there is a slight difference in behavior (based on the emulator): On Android, the access permission to a certain resource cannot be revoked once it is granted, even if it hasn't been selected when using <code>presentLimited</code> in future actions.</p> <h3 class="hash-header" id="get-albumsfolders-assetpathentity">Get albums/folders (<code>AssetPathEntity</code>) <a href="#get-albumsfolders-assetpathentity" class="hash-link">#</a></h3> <p>Albums or folders are abstracted as the <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/AssetPathEntity-class.html"><code>AssetPathEntity</code></a> class. It represents a bucket in the <code>MediaStore</code> on Android, and the <code>PHAssetCollection</code> object on iOS/macOS. To get all of them:</p> <pre><code class="language-dart">final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList(); </code></pre> <p>See <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/PhotoManager/getAssetPathList.html"><code>getAssetPathList</code></a> for more detail.</p> <h4 id="params-of-getassetpathlist">Params of <code>getAssetPathList</code></h4> <table> <thead> <tr> <th align="left">Name</th> <th>Description</th> <th>Default value</th> </tr> </thead> <tbody> <tr> <td align="left">hasAll</td> <td>Set to true when you need an album containing all assets.</td> <td>true</td> </tr> <tr> <td align="left">onlyAll</td> <td>Use this property if you only need one album containing all assets.</td> <td>false</td> </tr> <tr> <td align="left">type</td> <td>Type of media resource (video, image, audio)</td> <td>RequestType.common</td> </tr> <tr> <td align="left">filterOption</td> <td>Used to filter resource files, see <a href="#filtering">Filtering</a> for details</td> <td>FilterOptionGroup()</td> </tr> <tr> <td align="left">pathFilterOption</td> <td>Only valid for iOS and macOS, for the corresponding album type. See <a href="#pmpathfilteroption">PMPathFilterOption</a> for more detail.</td> <td>Include all by default.</td> </tr> </tbody> </table> <h4 id="pmpathfilteroption">PMPathFilterOption</h4> <p>Provide since 2.7.0, currently only valid for iOS and macOS.</p> <pre><code class="language-dart">final List<PMDarwinAssetCollectionType> pathTypeList = []; // use your need type final List<PMDarwinAssetCollectionSubtype> pathSubTypeList = []; // use your need type final darwinPathFilterOption = PMDarwinPathFilter( type: pathTypeList, subType: pathSubTypeList, ); PMPathFilter pathFilter = PMPathFilter(); </code></pre> <p>The <code>PMDarwinAssetCollectionType</code> has a one-to-one correspondence with <a href="https://developer.apple.com/documentation/photokit/phassetcollectiontype?language=objc" rel="ugc">PHAssetCollectionType | Apple Developer Documentation</a>.</p> <p>The <code>PMDarwinAssetCollectionSubtype</code> has a one-to-one correspondence with <a href="https://developer.apple.com/documentation/photokit/phassetcollectionsubtype?language=objc" rel="ugc">PHAssetCollectionSubType | Apple Developer Documentation</a>.</p> <h3 class="hash-header" id="get-assets-assetentity">Get assets (<code>AssetEntity</code>) <a href="#get-assets-assetentity" class="hash-link">#</a></h3> <p>Assets (images/videos/audios) are abstracted as the <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/AssetEntity-class.html"><code>AssetEntity</code></a> class. It represents a series of fields with <code>MediaStore</code> on Android, and the <code>PHAsset</code> object on iOS/macOS.</p> <h4 id="from-assetpathentity">From <code>AssetPathEntity</code></h4> <p>You can use <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/AssetPathEntity/getAssetListPaged.html">the pagination method</a>:</p> <pre><code class="language-dart">final List<AssetEntity> entities = await path.getAssetListPaged(page: 0, size: 80); </code></pre> <p>Or use <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/AssetPathEntity/getAssetListRange.html">the range method</a>:</p> <pre><code class="language-dart">final List<AssetEntity> entities = await path.getAssetListRange(start: 0, end: 80); </code></pre> <h4 id="from-photomanager-since-26">From <code>PhotoManager</code> (Since 2.6)</h4> <p>First, You need get count of assets:</p> <pre><code class="language-dart">final int count = await PhotoManager.getAssetCount(); </code></pre> <p>Then, you can use <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/PhotoManager/getAssetListPaged.html">the pagination method</a>:</p> <pre><code class="language-dart">final List<AssetEntity> entities = await PhotoManager.getAssetListPaged(page: 0, pageCount: 80); </code></pre> <p>Or use <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/PhotoManager/getAssetListRange.html">the range method</a>:</p> <pre><code class="language-dart">final List<AssetEntity> entities = await PhotoManager.getAssetListRange(start: 0, end: 80); </code></pre> <p><strong>Note:</strong> <code>page</code> and <code>start</code> starts from <strong>0</strong>.</p> <h4 id="from-id">From ID</h4> <p>The ID concept represents:</p> <ul> <li>The ID field of the <code>MediaStore</code> on Android.</li> <li>The <code>localIdentifier</code> field of the <code>PHAsset</code> on iOS.</li> </ul> <p>You can store the ID if you want to implement features that's related to persistent selections. Use <a href="https://pub.dev/documentation/photo_manager/latest/photo_manager/AssetEntity/fromId.html"><code>AssetEntity.fromId</code></a> to retrieve the entity once you persist an ID.</p> <pre><code class="language-dart">final AssetEntity? asset = await AssetEntity.fromId(id); </code></pre> <p>Be aware that the created asset might have limited access or got deleted in anytime, so the result might be null.</p> <h4 id="from-raw-data">From raw data</h4> <p>You can create an entity from raw data, such as downloaded images, recorded videos, etc. The created entity will show as a corresponding resource on your device's gallery app.</p> <pre><code class="language-dart">final Uint8List rawData = yourRawData; // Save an image to an entity from `Uint8List`. final AssetEntity? entity = await PhotoManager.editor.saveImage( rawData, title: 'write_your_own_title.jpg', // Affects EXIF reading. ); // Save an existed image to an entity from it's path. final AssetEntity? imageEntityWithPath = await PhotoManager.editor.saveImageWithPath( path, // Use the absolute path of your source file, it's more like a copy method. title: 'same_as_above.jpg', ); // Save a video entity from `File`. final File videoFile = File('path/to/your/video.mp4'); final AssetEntity? videoEntity = await PhotoManager.editor.saveVideo( videoFile, // You can check whether the file is exist for better test coverage. title: 'write_your_own_title.mp4', ); // [iOS only] Save a live photo from image and video `File`. // This only works when both image and video file were part of same live photo. final File imageFile = File('path/to/your/live_photo.heic'); final File videoFile = File('path/to/your/live_video.mp4'); final AssetEntity? entity = await PhotoManager.editor.darwin.saveLivePhoto( imageFile: imageFile, videoFile: videoFile, title: 'write_your_own_title.heic', ); </code></pre> <p>Be aware that the created asset might have limited access or got deleted in anytime, so the result might be null.</p> <p>iOS and macOS might set extra limitations when saving assets, it seems only certain file types can be saved as a media assets. The limitation follows the definition of <a href="https://developer.apple.com/documentation/avfoundation/avfiletype" rel="ugc"><code>AVFileType</code></a>, when you saw <code>PHPhotosErrorDomain Code=3302</code> (or <code>330x</code>), make sure the file type is supported.</p> <h4 id="from-icloud">From iCloud</h4> <p>Resources might be saved only on iCloud to save disk space. When retrieving file from iCloud, the speed is depended on the network condition, which might be very slow that makes users feel anxious. To provide a responsive user interface, you can use <code>PMProgressHandler</code> to retrieve the progress when load a file.</p> <p>The preferred implementation would be the <a href="https://github.com/fluttercandies/flutter_wechat_assets_picker/blob/2055adfa74370339d10e6f09adef72f2130d2380/lib/src/widget/builder/locally_available_builder.dart" rel="ugc"><code>LocallyAvailableBuilder</code></a> in the <code>wechat_asset_picker</code> package, which provides a progress indicator when the file is downloading.</p> <p>There are several methods that can combine with <code>PMProgressHandler</code> to provide responsive progress events, which are:</p> <ul> <li>AssetEntity.thumbnailDataWithSize</li> <li>AssetEntity.thumbnailDataWithOption</li> <li>AssetEntity.getMediaUrl</li> <li>AssetEntity.loadFile</li> <li>PhotoManager.plugin.getOriginBytes</li> </ul> <p>iCloud files can only be obtained when the Apple ID on the device are correctly authorized. When the account requires to re-enter the password to verify, iCloud files that are not locally available are not allowed to be fetched. The photo library will throws <code>CloudPhotoLibraryErrorDomain</code> in this circumstance.</p> <h4 id="display-assets">Display assets</h4> <blockquote> <p>Starts from v3.0.0, <code>AssetEntityImage</code> and <code>AssetEntityImageProvider</code> are provided in the <a href="https://pub.dev/packages/photo_manager_image_provider"><code>photo_manager_image_provider</code></a> package.</p> </blockquote> <p>Use the <code>AssetEntityImage</code> widget or the <code>AssetEntityImageProvider</code> to display assets:</p> <pre><code class="language-dart">import 'package:photo_manager_image_provider/photo_manager_image_provider.dart'; final Widget image = AssetEntityImage( yourAssetEntity, isOriginal: false, // Defaults to `true`. thumbnailSize: const ThumbnailSize.square(200), // Preferred value. thumbnailFormat: ThumbnailFormat.jpeg, // Defaults to `jpeg`. ); final Widget imageFromProvider = Image( image: AssetEntityImageProvider( yourAssetEntity, isOriginal: false, thumbnailSize: const ThumbnailSize.square(200), thumbnailFormat: ThumbnailFormat.jpeg, ), ); </code></pre> <h4 id="get-asset-files">Get asset files</h4> <p>There are several file getters and methods with an <code>AssetEntity</code>:</p> <ul> <li><code>.file</code></li> <li><code>.fileWithSubtype</code></li> <li><code>.originFile</code></li> <li><code>.originFileWithSubtype</code></li> <li><code>.loadFile</code></li> </ul> <p>These getters and methods will fetch different types of file related to that asset. Read their comment (documentation) to know their abilities.</p> <p>Additionally, you can use the raw plugin method <code>PhotoManager.plugin.getFullFile</code> with more parameters.</p> <h4 id="obtain-live-photos">Obtain "Live Photos"</h4> <p>This plugin supports obtain live photos and filtering them:</p> <h5 id="filtering-only-live-photos">Filtering only "Live Photos"</h5> <p>This is supported when filtering only image.</p> <pre><code class="language-dart">final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList( type: RequestType.image, filterOption: FilterOptionGroup(onlyLivePhotos: true), ); </code></pre> <p>Or you can use the <code>CustomSqlFilter</code> to obtain live photos:</p> <pre><code class="language-dart">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)', ), ); </code></pre> <h5 id="obtain-the-video-from-live-photos">Obtain the video from "Live Photos"</h5> <pre><code class="language-dart">final AssetEntity entity = livePhotoEntity; // To play Live Photo's video. final String? mediaUrl = await entity.getMediaUrl(); // Get files for normal displays like thumbnails. final File? imageFile = await entity.file; final File? videoFile = await entity.fileWithSubtype; // Get files for the raw displays like detail preview. final File? originImageFile = await entity.originFile; final File? originVideoFile = await entity.originFileWithSubtype; // Additionally, you can convert Live Photo's (on iOS) video file // from `mov` to `mp4` using: final File? convertedFile = await entity.loadFile( isOriginal: true, withSubtye: true, darwinFileType: PMDarwinAVFileType.mp4, ); </code></pre> <h4 id="limitations">Limitations</h4> <h5 id="android-10-media-location-permission">Android 10 media location permission</h5> <p>Due to the privacy policy issues on Android 10, it is necessary to grant the location permission to obtain the original data with the location info and the EXIF metadata.</p> <p>If you want to use the location permission, add the <code>ACCESS_MEDIA_LOCATION</code> permission to your manifest.</p> <h5 id="usage-of-the-original-data">Usage of the original data</h5> <p>The <code>originFile</code> and <code>originBytes</code> getter will return the original data of an entity. However, there are some cases that the original data is invalid in Flutter. Here are some common cases:</p> <ul> <li>HEIC files are not fully supported across platforms. We suggest you to upload the JPEG file (<code>.file</code> if the file is HEIC) in order to keep a consistent behavior between multiple platforms. See <a href="https://github.com/flutter/flutter/issues/20522" rel="ugc">flutter/flutter#20522</a> for more detail.</li> <li>Videos will only be obtained in the original format, not the exported/composited format, which might cause some behavior difference when playing videos.</li> </ul> <h5 id="long-retrieving-duration-with-file-on-ios">Long retrieving duration with file on iOS</h5> <p>There are several I/O methods in this library targeting <code>AssetEntity</code>, typically they are:</p> <ul> <li>All methods named with <code>file</code>.</li> <li><code>AssetEntity.originBytes</code>.</li> </ul> <p>File retrieving and caches are limited by the sandbox mechanism on iOS. An existing <code>PHAsset</code> doesn't mean the file located on the device. In generally, a <code>PHAsset</code> will have three status:</p> <ul> <li><code>isLocallyAvailable</code> equals <code>true</code>, <strong>also cached</strong>: Available for obtain.</li> <li><code>isLocallyAvailable</code> equals <code>true</code>, <strong>but not cached</strong>: When you call I/O methods, the resource will first cache into the sandbox, then available for obtain.</li> <li><code>isLocallyAvailable</code> equals <code>false</code>: Typically this means the asset exists, but it's saved only on iCloud, or some videos that not exported yet. In this case, the best practise is to use the <code>PMProgressHandler</code> to provide a responsive user interface.</li> </ul> <h3 class="hash-header" id="entities-change-notify">Entities change notify <a href="#entities-change-notify" class="hash-link">#</a></h3> <p>Plugin will post entities change events from native, but they will include different contents. See <a href="https://github.com/fluttercandies/flutter_photo_manager/blob/main/log" rel="ugc">the <code>logs</code> folder</a> for more recorded logs.</p> <p>To register a callback for these events, use <code>PhotoManager.addChangeCallback</code> to add a callback, and use <code>PhotoManager.removeChangeCallback</code> to remove the callback, just like <code>addListener</code> and <code>removeListener</code> methods.</p> <p>After you added/removed callbacks, you can call <code>PhotoManager.startChangeNotify</code> method to enable to notify, and <code>PhotoManager.stopChangeNotify</code> method to stop notify.</p> <pre><code class="language-dart">import 'package:flutter/services.dart'; void changeNotify(MethodCall call) { // Your custom callback. } /// Register your callback. PhotoManager.addChangeCallback(changeNotify); /// Enable change notify. PhotoManager.startChangeNotify(); /// Remove your callback. PhotoManager.removeChangeCallback(changeNotify); /// Disable change notify. PhotoManager.stopChangeNotify(); </code></pre> <h2 class="hash-header" id="filtering">Filtering <a href="#filtering" class="hash-link">#</a></h2> <p>Filtering assets are also supported by the plugin. Below methods have the <code>filterOption</code> argument to define the conditions when obtaining assets.</p> <ul> <li>PhotoManager <ul> <li>getAssetPathList (Accessible with <code>AssetPathEntity.filterOption</code>)</li> <li>getAssetCount</li> <li>getAssetListRange</li> <li>getAssetListPaged</li> </ul> </li> <li>AssetPathEntity <ul> <li>constructor (not recommended to use directly)</li> <li>fromId</li> <li>obtainPathFromProperties (not recommended to use directly)</li> </ul> </li> </ul> <p>There are two implementations of filters:</p> <ul> <li><a href="#filteroptiongroup">FilterOptionGroup</a></li> <li><a href="#customfilter">CustomFilter</a></li> </ul> <h3 class="hash-header" id="filteroptiongroup">FilterOptionGroup <a href="#filteroptiongroup" class="hash-link">#</a></h3> <p>The <code>FilterOptionGroup</code> is the only implementation before v2.6.0.</p> <pre><code class="language-dart">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, ), ], /// other options ); </code></pre> <h3 class="hash-header" id="customfilter">CustomFilter <a href="#customfilter" class="hash-link">#</a></h3> <p><strong>NOTE:</strong> The <code>CustomFilter</code> is still a newbie of the plugin that introduced in v2.6.0, please help us to improve it by submitting issues if you've met any related problems.</p> <p><code>CustomFilter</code> provides flexible filtering conditions, and it's targeting host platforms. The usages of the filter are more look close to native integrations, which is a <code>SQL-like</code> filter.</p> <p>The SQL columns name on Android and Darwin (iOS/macOS) are different, you'll need to use <code>CustomColumns.base</code>, <code>CustomColumns.android</code> and <code>CustomColumns.darwin</code> to get the columns name correctly.</p> <p>An example for how to construct a <code>CustomFilter</code>:</p> <pre><code class="language-dart">CustomFilter createFilter() { return CustomFilter.sql( where: '${CustomColumns.base.width} > 100 AND ${CustomColumns.base.height} > 200', orderBy: [OrderByItem.desc(CustomColumns.base.createDate)], ); } </code></pre> <h4 id="advanced-customfilter">Advanced CustomFilter</h4> <p><code>AdvancedCustomFilter</code> is a subclass of <code>CustomFilter</code>, it has builder methods to help create the filter.</p> <pre><code class="language-dart">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; } </code></pre> <h4 id="classes-explanations">Classes explanations</h4> <ul> <li><code>CustomFilter</code>: The base class of custom filter.</li> <li><code>SqlCustomFilter</code>: Creates a SQL-like filter.</li> <li><code>AdvancedCustomFilter</code>: Creates an advanced filter. <ul> <li><code>OrderByItem</code>: The class of ORDER BY item.</li> <li><code>WhereConditionItem</code>: The class of WHERE condition item. <ul> <li><code>WhereConditionGroup</code>: Create a group of where condition.</li> <li><code>TextWhereCondition</code>: The text will not be verified if it's valid.</li> <li><code>ColumnWhereCondition</code>: The input column will be verified if it's valid.</li> <li><code>DateColumnWhereCondition</code>: Dates have different conversion methods on iOS/macOS, it helps to smoothest the platform differences.</li> </ul> </li> </ul> </li> <li><code>CustomColumns</code>: This class contains fields for different platforms. <ul> <li><code>base</code>: The common fields are included here, but please note that the "id" field is invalid in iOS and may even cause errors. It is only valid on Android.</li> <li><code>android</code>: The columns of the Android platform.</li> <li><code>darwin</code>: The columns of iOS/macOS platforms.</li> </ul> </li> </ul> <p>Here is the flow chart to explain how advanced filter works: <img src="https://github.com/fluttercandies/flutter_photo_manager/raw/main/flow_chart/advance_custom_filter.png" alt="flow_chart"></p> <h2 class="hash-header" id="cache-mechanism">Cache mechanism <a href="#cache-mechanism" class="hash-link">#</a></h2> <h3 class="hash-header" id="cache-on-android">Cache on Android <a href="#cache-on-android" class="hash-link">#</a></h3> <p>Because Android 10 restricts the ability to access the resource path directly, image caches will be generated during I/O processes. More specifically, when the <code>file</code>, <code>originFile</code> and any other I/O getters are called, the plugin will save a file in the cache folder for further use.</p> <p>Fortunately, on Android 11 and above, the resource path can be obtained directly again, but you can still use <code>requestLegacyExternalStorage</code> to access files in the storage without caching them. See <a href="#android-10-q-29">Android 10 (Q, 29)</a> for how to add the attribute. The attribute is not required.</p> <h3 class="hash-header" id="cache-on-ios">Cache on iOS <a href="#cache-on-ios" class="hash-link">#</a></h3> <p>iOS does not directly provide APIs to access the original files of the album. So a cached file will be generated locally into the container of the current application when you called <code>file</code>, <code>originFile</code> and any other I/O getters.</p> <p>If occupied disk spaces are sensitive in your use case, you can delete it after your usage has done (iOS only).</p> <pre><code class="language-dart">import 'dart:io'; Future<void> useEntity(AssetEntity entity) async { File? file; try { file = await entity.file; await handleFile(file!); // Custom method to handle the obtained file. } finally { if (Platform.isIOS) { file?.deleteSync(); // Delete it once the process has done. } } } </code></pre> <h3 class="hash-header" id="clear-caches">Clear caches <a href="#clear-caches" class="hash-link">#</a></h3> <p>You can use the <code>PhotoManager.clearFileCache()</code> method to clear all caches that generated by the plugin. Here are caches generation on different platforms, types and resolutions.</p> <table> <thead> <tr> <th>Platform</th> <th>Thumbnail</th> <th>File / Origin File</th> </tr> </thead> <tbody> <tr> <td>Android</td> <td>Yes</td> <td>Yes (Android 10+)</td> </tr> <tr> <td>iOS</td> <td>No</td> <td>Yes</td> </tr> </tbody> </table> <h2 class="hash-header" id="native-extra-configs">Native extra configs <a href="#native-extra-configs" class="hash-link">#</a></h2> <h3 class="hash-header" id="android-extra-configs">Android extra configs <a href="#android-extra-configs" class="hash-link">#</a></h3> <h4 id="glide-issues">Glide issues</h4> <p>If your found any conflicting issues against Glide, then you'll need to edit the <code>android/build.gradle</code> file:</p> <pre><code class="language-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.15.1' } } } } } </code></pre> <p>See <a href="https://github.com/bumptech/glide#proguard" rel="ugc">ProGuard for Glide</a> if you want to know more about using ProGuard and Glide together.</p> <h4 id="android-14-api-34-extra-configs">Android 14 (API 34) extra configs</h4> <p>When running on Android 14 (API level 34), the following permissions needs to be added to the manifest even if your <code>targetSdkVersion</code> and <code>compileSdkVersion</code> is not <code>34</code>:</p> <pre><code class="language-xml"><manifest> <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" /> <!-- If you want to use the limited access feature. --> </manifest> </code></pre> <h4 id="android-13-api-33-extra-configs">Android 13 (API 33) extra configs</h4> <p>When running on Android 13 (API level 33), the following permissions needs to be added to the manifest even if your <code>targetSdkVersion</code> and <code>compileSdkVersion</code> is not <code>33</code>:</p> <blockquote> <p>Note: <code>READ_MEDIA_IMAGES</code> and <code>READ_MEDIA_VIDEO</code> are both required whether requesting images or videos.</p> </blockquote> <pre><code class="language-xml"><manifest> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- If you want to read images or videos--> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- If you want to read videos or images--> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- If you want to read audio--> </manifest> </code></pre> <h3 class="hash-header" id="ios-extra-configs">iOS extra configs <a href="#ios-extra-configs" class="hash-link">#</a></h3> <h4 id="localized-system-albums-name">Localized system albums name</h4> <p>By default, iOS will retrieve system album names only in English no matter what language has been set to devices. To change the default language, see the following steps:</p> <ul> <li> <p>Open your iOS project (Runner.xcworkspace) using Xcode. <img src="https://raw.githubusercontent.com/CaiJingLong/some_asset/master/iosFlutterProjectEditinginXcode.png" alt="Edit localizations in Xcode 1"></p> </li> <li> <p>Select the project "Runner" and in the localizations table, click on the + icon. <img src="https://raw.githubusercontent.com/CaiJingLong/some_asset/master/iosFlutterAddLocalization.png" alt="Edit localizations in Xcode 2"></p> </li> <li> <p>Select the adequate language(s) you want to retrieve localized strings.</p> </li> <li> <p>Validate the popup screen without any modification.</p> </li> <li> <p>Rebuild your flutter project.</p> </li> </ul> <p>Now system albums label should display accordingly.</p> <p><strong>Note</strong>: Localize is not meant to customize albums name.</p> <h3 class="hash-header" id="experimental-features">Experimental features <a href="#experimental-features" class="hash-link">#</a></h3> <p><strong>Warning</strong>: Features here aren't guaranteed to be fully usable across platforms and OS versions, since they involved with data modification. They can be modified/removed in any time, without following a proper version semantic.</p> <p>Some APIs will make irreversible modification/deletion to data. <strong>Please be careful and implement your own test mechanism when using them</strong>.</p> <h4 id="preload-thumbnails">Preload thumbnails</h4> <p>You can preload thumbnails for entities with specified thumbnail options using <code>PhotoCachingManager.requestCacheAssets</code> or <code>PhotoCachingManager.requestCacheAssetsWithIds</code>.</p> <pre><code class="language-dart">PhotoCachingManager().requestCacheAssets(assets: assets, option: option); </code></pre> <p>And you can stop in anytime by calling <code>PhotoCachingManager().cancelCacheRequest()</code>.</p> <p>Usually, when we're previewing assets, thumbnails will be loaded instantly. But sometimes we want to preload assets to make them display faster.</p> <p>The <code>PhotoCachingManager</code> uses the <a href="https://developer.apple.com/documentation/photokit/phcachingimagemanager?language=objc" rel="ugc">PHCachingImageManager</a> on iOS, and Glide's file cache on Android.</p> <h4 id="delete-entities">Delete entities</h4> <p><strong>This method will delete the asset completely from your device. Use it with extra cautious.</strong></p> <pre><code class="language-dart">// Deleted IDs will returned, if it fails, the result will be an empty list. final List<String> result = await PhotoManager.editor.deleteWithIds( <String>[entity.id], ); </code></pre> <p>After the deletion, you can call the <code>refreshPathProperties</code> method to refresh the corresponding <code>AssetPathEntity</code> in order to get latest fields.</p> <h4 id="copy-an-entity">Copy an entity</h4> <p>You can use <code>copyAssetToPath</code> method to "Copy" an entity from its current position to the targeting <code>AssetPathEntity</code>:</p> <pre><code class="language-dart">// Make sure your path entity is accessible. final AssetPathEntity anotherPathEntity = anotherAccessiblePath; final AssetEntity entity = yourEntity; final AssetEntity? newEntity = await PhotoManager.editor.copyAssetToPath( asset: entity, pathEntity: anotherPathEntity, ); // The result could be null when the path is not accessible. </code></pre> <p>The "Copy" means differently here on Android and iOS:</p> <ul> <li>For Android, it inserts a copy of the source entity: <ul> <li>On platforms <=28, the method will copy most of the origin info.</li> <li>On platforms >=29, some fields cannot be modified during the insertion, e.g. <a href="https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#RELATIVE_PATH" rel="ugc">MediaColumns.RELATIVE_PATH</a>.</li> </ul> </li> <li>For iOS, it makes a shortcut thing rather than create a new physical entity. <ul> <li>Some albums are smart albums, their content is automatically managed by the system and cannot insert entities manually.</li> </ul> </li> </ul> <p>(For Android 30+, this feature is blocked by system limitations currently.)</p> <h4 id="features-for-android-only">Features for Android only</h4> <h5 id="move-an-entity-to-another-album">Move an entity to another album</h5> <pre><code class="language-dart">// Make sure your path entity is accessible. final AssetPathEntity pathEntity = accessiblePath; final AssetEntity entity = yourEntity; await PhotoManager.editor.android.moveAssetToAnother( entity: entity, target: pathEntity, ); </code></pre> <p>(For Android 30+, this feature is blocked by system limitations currently.)</p> <h5 id="remove-all-non-exist-entities">Remove all non-exist entities</h5> <p>This will remove all items (records) that's not existed locally. A record in Android <code>MediaStore</code> could have the corresponding file deleted. Those abnormal behaviors usually caused by operations from file manager, helper tools or adb tool. This operation is resource-consuming, Please use the <code>await</code> keyword to call the cleaning process before you call another one.</p> <pre><code class="language-dart">await PhotoManager.editor.android.removeAllNoExistsAsset(); </code></pre> <p>Some operating systems will prompt confirmation dialogs for each entities' deletion, we have no way to avoid them. Make sure you're using the correct method, and your customers accept repeatedly confirmations.</p> <h5 id="move-entities-to-trash">Move entities to trash</h5> <pre><code class="language-dart">await PhotoManager.editor.android.moveToTrash(list); </code></pre> <p>The method supports Android 11 and above versions. It will move the entities to trash. Throws exception when calling on Android 11-.</p> <h4 id="features-for-ios-or-macos">Features for iOS or macOS</h4> <h5 id="create-a-folder">Create a folder</h5> <pre><code class="language-dart">PhotoManager.editor.darwin.createFolder( name, parent: parent, // The value should be null, the root path or other accessible folders. ); </code></pre> <h5 id="create-an-album">Create an album</h5> <pre><code class="language-dart">PhotoManager.editor.darwin.createAlbum( name, parent: parent, // The value should be null, the root path or other accessible folders. ); </code></pre> <h5 id="remove-the-entity-entry-from-the-album">Remove the entity entry from the album</h5> <p>Remove the entry of the asset from the specific album. The asset won't be deleted from the device, only removed from the album.</p> <pre><code class="language-dart">// Make sure your path entity is accessible. final AssetPathEntity pathEntity = accessiblePath; final AssetEntity entity = yourEntity; final List<AssetEntity> entities = <AssetEntity>[yourEntity, anotherEntity]; // Remove single asset from the album. // It'll call the list method as the implementation. await PhotoManager.editor.darwin.removeInAlbum( yourEntity, accessiblePath, ); // Remove assets from the album in batches. await PhotoManager.editor.darwin.removeAssetsInAlbum( entities, accessiblePath, ); </code></pre> <h5 id="delete-assetpathentity">Delete <code>AssetPathEntity</code></h5> <p>Smart albums can't be deleted.</p> <pre><code class="language-dart">PhotoManager.editor.darwin.deletePath(); </code></pre> <h4 id="features-for-openharmony">Features for OpenHarmony</h4> <blockquote> <p>The photo library feature is disabled in OpenHarmony officially because of the security concern.</p> </blockquote> <p>Most functions are supported except caching, and only images/videos are supported.</p> <table> <thead> <tr> <th align="left">Feature</th> <th align="center">OpenHarmony</th> </tr> </thead> <tbody> <tr> <td align="left">releaseCache</td> <td align="center">❌</td> </tr> <tr> <td align="left">clearFileCache</td> <td align="center">❌</td> </tr> <tr> <td align="left">requestCacheAssetsThumbnail</td> <td align="center">❌</td> </tr> <tr> <td align="left">getSubPathEntities</td> <td align="center">❌</td> </tr> </tbody> </table> </section></div></div></div><aside class="detail-info-box"><a class="packages-scores" href="/packages/photo_manager/score"><div class="packages-score packages-score-like"><div class="packages-score-value -has-value"><span class="packages-score-value-number">678</span><span class="packages-score-value-sign"></span></div><div class="packages-score-label">likes</div></div><div class="packages-score packages-score-health"><div class="packages-score-value -has-value"><span class="packages-score-value-number">150</span><span class="packages-score-value-sign"></span></div><div class="packages-score-label">pub points</div></div><div class="packages-score packages-score-popularity"><div class="packages-score-value -has-value"><span class="packages-score-value-number">99</span><span class="packages-score-value-sign">%</span></div><div class="packages-score-label">popularity</div></div></a><h3 class="title">Publisher</h3><p><a href="/publishers/fluttercandies.com"><img class="-pub-publisher-shield filter-invert-on-dark" src="/static/hash-o6oemknr/img/material-icon-verified.svg" alt="verified publisher" width="14" height="14" title="Published by a pub.dev verified publisher"/>fluttercandies.com</a></p><h3 class="title pkg-infobox-metadata">Metadata</h3><p>A Flutter plugin that provides album assets abstraction management APIs on Android, iOS, macOS, and OpenHarmony.</p><p><a class="link" href="https://github.com/fluttercandies/flutter_photo_manager" rel="ugc">Repository (GitHub)</a><br/><a class="link" href="https://github.com/fluttercandies/flutter_photo_manager/issues" rel="ugc">View/report issues</a><br/></p><h3 class="title">Documentation</h3><p><a class="link" href="/documentation/photo_manager/latest/">API reference</a><br/></p><h3 class="title">License</h3><p><img class="inline-icon-img filter-invert-on-dark" src="/static/hash-o6oemknr/img/material-icon-balance.svg" alt="" width="14" height="14" role="presentation"/>Apache-2.0 (<a href="/packages/photo_manager/license">license</a>)</p><h3 class="title">Dependencies</h3><p><a href="https://api.flutter.dev/">flutter</a></p><h3 class="title">More</h3><p><a href="/packages?q=dependency%3Aphoto_manager" rel="nofollow">Packages that depend on photo_manager</a></p></aside></div><script type="application/ld+json">{"@context":"http\u003a\u002f\u002fschema.org","@type":"SoftwareSourceCode","name":"photo\u005fmanager","version":"3.6.3","description":"photo\u005fmanager - A Flutter plugin that provides album assets abstraction management APIs on Android, iOS, macOS, and OpenHarmony.","url":"https\u003a\u002f\u002fpub.dev\u002fpackages\u002fphoto\u005fmanager","dateCreated":"2018-10-02T08\u003a50\u003a07.037931Z","dateModified":"2024-11-21T11\u003a34\u003a10.822387Z","programmingLanguage":"Dart","image":"https\u003a\u002f\u002fpub.dev\u002fstatic\u002fimg\u002fpub-dev-icon-cover-image.png","license":"https\u003a\u002f\u002fpub.dev\u002fpackages\u002fphoto\u005fmanager\u002flicense"}</script></div><div class="detail-metadata"><h3 class="detail-metadata-title"><span class="detail-metadata-toggle">←</span> Metadata</h3><div class="detail-info-box"><a class="packages-scores" href="/packages/photo_manager/score"><div class="packages-score packages-score-like"><div class="packages-score-value -has-value"><span class="packages-score-value-number">678</span><span class="packages-score-value-sign"></span></div><div class="packages-score-label">likes</div></div><div class="packages-score packages-score-health"><div class="packages-score-value -has-value"><span class="packages-score-value-number">150</span><span class="packages-score-value-sign"></span></div><div class="packages-score-label">pub points</div></div><div class="packages-score packages-score-popularity"><div class="packages-score-value -has-value"><span class="packages-score-value-number">99</span><span class="packages-score-value-sign">%</span></div><div class="packages-score-label">popularity</div></div></a><h3 class="title">Publisher</h3><p><a href="/publishers/fluttercandies.com"><img class="-pub-publisher-shield filter-invert-on-dark" src="/static/hash-o6oemknr/img/material-icon-verified.svg" alt="verified publisher" width="14" height="14" title="Published by a pub.dev verified publisher"/>fluttercandies.com</a></p><h3 class="title pkg-infobox-metadata">Metadata</h3><p>A Flutter plugin that provides album assets abstraction management APIs on Android, iOS, macOS, and OpenHarmony.</p><p><a class="link" href="https://github.com/fluttercandies/flutter_photo_manager" rel="ugc">Repository (GitHub)</a><br/><a class="link" href="https://github.com/fluttercandies/flutter_photo_manager/issues" rel="ugc">View/report issues</a><br/></p><h3 class="title">Documentation</h3><p><a class="link" href="/documentation/photo_manager/latest/">API reference</a><br/></p><h3 class="title">License</h3><p><img class="inline-icon-img filter-invert-on-dark" src="/static/hash-o6oemknr/img/material-icon-balance.svg" alt="" width="14" height="14" role="presentation"/>Apache-2.0 (<a href="/packages/photo_manager/license">license</a>)</p><h3 class="title">Dependencies</h3><p><a href="https://api.flutter.dev/">flutter</a></p><h3 class="title">More</h3><p><a href="/packages?q=dependency%3Aphoto_manager" rel="nofollow">Packages that depend on photo_manager</a></p></div><p class="detail-lead-back"><a class="detail-metadata-toggle">Back</a></p></div><div id="-screenshot-carousel" class="carousel"><fab id="-carousel-prev" class="mdc-fab carousel-prev carousel-nav" data-mdc-auto-init="MDCRipple" title="Previous" data-ga-click-event="screenshot-carousel-prev-click" tabindex="0"><div class="mdc-fab__ripple"></div><img class="mdc-fab__icon" src="/static/hash-o6oemknr/img/keyboard_arrow_left.svg" alt="previous" width="24" height="24" aria-hidden="true"/></fab><div id="-image-container" class="image-container"></div><fab id="-carousel-next" class="mdc-fab carousel-next carousel-nav" data-mdc-auto-init="MDCRipple" title="Next" data-ga-click-event="screenshot-carousel-next-click" tabindex="0"><div class="mdc-fab__ripple"></div><img class="mdc-fab__icon" src="/static/hash-o6oemknr/img/keyboard_arrow_right.svg" alt="next" width="24" height="24" aria-hidden="true"/></fab><p id="-screenshot-description" class="screenshot-description"></p></div></main><footer class="site-footer"><a class="link" href="https://dart.dev/">Dart language</a><a class="link sep" href="/report?subject=package%3Aphoto_manager&url=https%3A%2F%2Fpub.dev%2Fpackages%2Fphoto_manager">Report package</a><a class="link sep" href="/policy">Policy</a><a class="link sep" href="https://www.google.com/intl/en/policies/terms/">Terms</a><a class="link sep" href="https://developers.google.com/terms/">API Terms</a><a class="link sep" href="/security">Security</a><a class="link sep" href="https://www.google.com/intl/en/policies/privacy/">Privacy</a><a class="link sep" href="/help">Help</a><a class="link icon sep" href="/feed.atom"><img class="inline-icon" src="/static/hash-o6oemknr/img/rss-feed-icon.svg" alt="RSS" width="20" height="20" title="RSS/atom feed"/></a><a class="link icon github_issue" href="https://github.com/dart-lang/pub-dev/issues/new"><img class="inline-icon" src="/static/hash-o6oemknr/img/bug-report-white-96px.png" alt="bug report" width="20" height="20" title="Report an issue with this site"/></a></footer><script src="/static/hash-o6oemknr/highlight/highlight-with-init.js" defer="defer"></script></body></html>