photo_manager

repository·main·Indexed 20 days ago

https://github.com/fluttercandies/flutter_photo_manager

A Flutter plugin providing an abstraction layer for managing media assets (images, videos, audio) across Android, iOS, macOS, and OpenHarmony. It enables retrieving albums via AssetPathEntity, managing assets via AssetEntity, saving new media to the gallery, and implementing advanced SQL-like filtering with CustomFilter and AdvancedCustomFilter. The plugin supports limited photo library access for iOS 14+ and Android 14+, iCloud asset handling, and provides mechanisms for managing file caches.

Tokens
22.3K
Snippets
85
Records
105
Agent score
69%

What's inside photo_manager

  1. Understand the plugin's caching mechanism

    main

    The plugin uses a caching mechanism to handle file access across different platforms:

    Android

    • Android 10 (Q, 29): Direct access to resource paths is restricted. The plugin generates image caches during I/O operations (e.g., when calling .file or .originFile). You can use requestLegacyExternalStorage to access files without caching.
    • Android 11+: Direct access to resource paths is available again.

    iOS

    • iOS does not provide an API to access original files directly. When calling .file or .originFile, the plugin generates a cache file within the application's sandbox.
    • Important: If disk space is a concern, you should manually delete these cache files after use.

    Clearing Cache

    You can clear all plugin-generated caches using PhotoManager.clearFileCache.

    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(); // 处理完成后删除
        }
      }
    }
  2. Handle iCloud assets and loading progress

    main

    Assets stored in iCloud may not be locally available. Retrieving them requires a network download, which can be slow. To maintain a responsive UI, use PMProgressHandler with methods that support progress reporting:

    • AssetEntity.thumbnailDataWithSize
    • AssetEntity.thumbnailDataWithOption
    • AssetEntity.getMediaUrl
    • AssetEntity.loadFile
    • PhotoManager.plugin.getOriginBytes

    If the user's Apple ID requires re-authentication, iCloud files cannot be fetched and the plugin will throw a CloudPhotoLibraryErrorDomain error.

  3. Manage file caches on iOS and Android

    main

    The plugin generates local cache files when accessing I/O getters like entity.file or entity.originFile due to platform restrictions.

    Android Cache

    On Android 10, caches are generated during I/O. On Android 11+, resource paths can be accessed directly, but you can still use requestLegacyExternalStorage in your manifest to avoid caching.

    iOS Cache

    iOS requires a cached file to be generated in the application's container. If disk space is a concern, you should manually delete the file after use.

    Clearing All Caches

    You can clear all plugin-generated caches (thumbnails and files) using PhotoManager.clearFileCache().

    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.
        }
      }
    }
  4. Handle iCloud and loading progress

    main

    On iOS, assets may reside only in iCloud. Fetching these files can be slow depending on network conditions. To prevent a poor user experience, use a PMProgressHandler to provide feedback to the user while loading files.

    Methods that support progress feedback via PMProgressHandler include:

    • AssetEntity.thumbnailDataWithSize
    • AssetEntity.thumbnailDataWithOption
    • AssetEntity.getMediaUrl
    • AssetEntity.loadFile
    • PhotoManager.plugin.getOriginBytes
  5. Handle limited photo library access on iOS and Android

    main

    Both iOS 14+ and Android 14+ support a "Limited Photos Library" where users select only specific assets for the app to access.

    iOS

    • Use PhotoManager.presentLimited() to show a modal allowing users to manage/reselect accessible entities.
    • To prevent the system from automatically prompting the user every time the app restarts, add the PHPhotoLibraryPreventAutomaticLimitedAccessAlert key to your Info.plist with a value of <true/>.

    Android

    • On Android 14, access to a specific resource cannot be revoked once granted, even if not selected via presentLimited in future actions.

    Recovery from mismatched media types

    If a user grants limited access but only selects videos when your app requested images, the query will return zero results. You should prompt for reselection using presentLimited with the specific RequestType needed.

    if (state == PermissionState.limited) {
      // On Android 14+, `type` filters the picker to only the media kinds you need.
      // On iOS the picker cannot be filtered by type and `type` is ignored.
      await PhotoManager.presentLimited(type: RequestType.image);
    }
  6. Configure Android for photo_manager

    main

    Kotlin, Gradle, and AGP Requirements

    The plugin requires Kotlin 1.7.22. If your project uses older versions, upgrade them to the following minimums:

    • Gradle version (gradle-wrapper.properties): 7.5.1 or later.
    • Kotlin version (ext.kotlin_version): 1.7.22 or later.
    • AGP version (com.android.tools.build:gradle): 7.2.2 or later.

    Android 10 (API 29) Scoped Storage

    If your compileSdkVersion or targetSdkVersion is 29, you may need to add android:requestLegacyExternalStorage="true" to your AndroidManifest.xml to access origin resource files directly.

    Warning: Apps using this flag may be rejected from Google Play. A better practice is to use the plugin's caching mechanism and call PhotoManager.clearFileCache() when starting your app.

    Glide Thumbnails

    The plugin uses [Glide] to generate thumbnail bytes on Android. If you see Glide warning logs, your main project may need to implement AppGlideModule.

    <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>
  7. Save images or videos with location on Darwin (iOS/macOS)

    main

    In version 3.12, the Darwin implementation no longer links CoreLocation. Consequently, passing latitude or longitude to PhotoManager.editor.saveImage, saveImageWithPath, or saveVideo will throw an error.

    To continue saving assets with location data on iOS/macOS, you must install and use the photo_manager_location plugin. Android remains unaffected.

    Note: Apps that do not save assets with location no longer require the NSLocationWhenInUseUsageDescription purpose string.

    import 'package:photo_manager_location/photo_manager_location.dart';
    
    final entity = await PhotoManager.editor.saveImage(
      bytes,
      filename: 'photo.jpg',
      latitude: 37.7749,
      longitude: -122.4194,
    );
    // ^ This will throw on Darwin in 3.12+
    
    // Use this instead:
    final entity = await PhotoManagerLocation.editor.saveImage(
      bytes,
      filename: 'photo.jpg',
      latitude: 37.7749,
      longitude: -122.4194,
    );
  8. Access Darwin album types via albumTypeEx

    main

    In version 3.1, AssetPathEntity.darwinType and AssetPathEntity.darwinSubtype were deprecated. You should now use the albumTypeEx property to access Darwin-specific information.

    final path = await AssetPathEntity.fromId('');
    final PMDarwinAssetCollectionType? darwinType = path.albumTypeEx?.darwin?.type;
    final PMDarwinAssetCollectionSubtype? darwinSubtype = path.albumTypeEx?.darwin?.subtype;
  9. Configure Android permissions and Glide

    main

    Depending on your target Android version, you must add specific permissions to your AndroidManifest.xml.

    Android 14 (API 34)

    Add the following for optional resource selection support:

    <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

    Android 13 (API 33)

    Add these permissions to read images, videos, or audio:

    <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" />

    Android 12 (API 31) - MANAGE_MEDIA (Optional)

    To allow batch operations (like deleteWithIds, moveToTrash, or favorites) without repeated system confirmation dialogs, declare MANAGE_MEDIA. Users must manually enable this in system settings.

    <uses-permission android:name="android.permission.MANAGE_MEDIA" />

    Use PhotoManager.canManageMedia() to check status and PhotoManager.requestManageMedia() to direct users to settings.

    Glide Version Conflicts

    If you encounter Glide version conflicts, add this resolution strategy to your android/build.gradle:

    rootProject.allprojects {
        subprojects {
            project.configurations.all {
                resolutionStrategy.eachDependency {
                    if (details.requested.group == 'com.github.bumptech.glide' 
                            && details.requested.name.contains('glide')) {
                        details.useVersion '4.14.2'
                    }
                }
            }
        }
    }
    <manifest>
       <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
    </manifest>
  10. Use MANAGE_MEDIA permission on Android 12+

    main

    On Android 12 (API 31) and above, you can opt-in to the MANAGE_MEDIA permission. When granted, operations like PhotoManager.editor.deleteWithIds, moveToTrash, and favorite operations will no longer show a system confirmation dialog for every call. This is ideal for bulk management apps.

    1. Declare the permission in AndroidManifest.xml:
    <uses-permission android:name="android.permission.MANAGE_MEDIA" />
    1. The user must manually enable this in Settings > Apps > Special app access > Media management.
    2. Use the following API to check status and route the user to settings:
    if (!await PhotoManager.canManageMedia()) {
      await PhotoManager.requestManageMedia();
      // Re-check canManageMedia() after the user returns from Settings.
    }

    Note: MANAGE_MEDIA only suppresses dialogs; READ_MEDIA_* and ACCESS_MEDIA_LOCATION are still required prerequisites.

    if (!await PhotoManager.canManageMedia()) {
      await PhotoManager.requestManageMedia();
      // Re-check canManageMedia() after the user returns from Settings.
    }
  11. Install photo_manager

    main

    You can add photo_manager to your Flutter project using one of two methods:

    1. Recommended: Run the following command in your terminal:
      flutter pub add photo_manager
    2. Manual: Add it directly to your pubspec.yaml under dependencies:
      dependencies:
        photo_manager: $latest_version
    dependencies:
      photo_manager: $latest_version