Flutter WeChat Assets Picker

pub package pub package Build status CodeFactor GitHub license

Awesome Flutter GitHub stars GitHub forks FlutterCandies

Language: English | 中文įŽ€äŊ“

An assets picker which looks like the one in WeChat, based on photo_manager for asset implementation, extended_image for image preview, and provider to help control the state of the picker.

To take a photo or a video for assets, please check the detailed usage in the example, and head over to wechat_camera_picker .

All UI designs are based on WeChat 7.x, and it will be updated following the WeChat update in anytime.

Note: You can file PRs to create your own implementation if you found your implementation might be useful for others. See Contribute custom implementations for more details.

Category 🗂

Migration Guide â™ģī¸

See Migration Guide.

Features ✨

  • â™ģī¸ Fully implementable with delegates override
  • 💚 99% simillar to WeChat style
  • ⚡ī¸ Adjustable performance according to parameters
  • 📷 Image asset support
    • đŸ”Ŧ HEIC/HEIF Image type support
  • đŸŽĨ Video asset support
  • đŸŽļ Audio asset support
  • 1ī¸âƒŖ Single asset mode
  • 💱 i18n support
    • âĒ RTL language support
  • ➕ Special item builder (prepend/append) support
  • 🗂 Custom sort path delegate support
  • 📝 Custom text delegate support
  • âŗ Custom filter options support ( photo_manager )
  • 🎏 Custom theme entirely
  • đŸ’ģ MacOS support

Screenshots 📸

1 2 3
4 5 6
7 8 9

READ THIS FIRST â€ŧī¸

Although the package provides assets selection, it still requires users to build their own methods to handle upload, image compress, etc. If you have any questions about how to build them, please run the example or refer to photo_manager for API usage.

Preparing for use 🍭

Version constraints

Flutter SDK: >=2.0.0 .

If you got a resolve conflict error when running flutter pub get , please use dependency_overrides to fix it. See here .

Flutter

Add wechat_assets_picker to pubspec.yaml dependencies.

dependencies:
  wechat_assets_picker: ^latest_version

The latest stable version is: pub package

The latest dev version is: pub package

Then import the package in your code:

import 'package:wechat_assets_picker/wechat_assets_picker.dart';

Android

Required permissions: INTERNET, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, ACCESS_MEDIA_LOCATION.

If you found some warning logs with Glide appearing, then the main project needs an implementation of AppGlideModule. See Generated API.

iOS

  1. Platform version has to be at least 9.0. Modify ios/Podfile and update accordingly.
platform :ios, '9.0'
  1. Add the following content to info.plist.
<key>NSAppTransportSecurity</key>
<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>Replace with your permission description.</string>

MacOS

  1. Platform version has to be at least 10.15. Modify macos/Podfile and update accordingly.
platform :osx, '10.15'
  1. Set the minimum deployment target to 10.15. Use XCode to open macos/Runner.xcworkspace .

  2. step 1

  3. step 2

  4. Follow the iOS instructions and modify info.plist accordingly.

Usage 📖

Name Type Description Default
selectedAssets List<AssetEntity>? Selected assets. Prevent duplicate selection. If you don't need to prevent duplicate selection, just don't pass it. null
maxAssets int Maximum asset that the picker can pick. 9
pageSize int? Number of assets per page. Must be a multiple of gridCount. 320 (80 * 4)
gridThumbSize int Thumbnail size for the grid's item. 200
pathThumbSize int Thumbnail size for the path selector. 80
previewThumbSize List<int>? Preview thumbnail size in the viewer. null
gridCount int Grid count in picker. 4
requestType RequestType Request type for picker. RequestType.image
specialPickerType SpacialPickerType? Provides the option to integrate a custom picker type. null
themeColor Color? Main theme color for the picker. Color(0xff00bc56)
pickerTheme ThemeData? Theme data provider for the picker and the viewer. null
sortPathDelegate SortPathDeleage? Path entities sort delegate for the picker, sort paths as you want. CommonSortPathDelegate
textDelegate AssetsPickerTextDelegate? Text delegate for the picker, for customize the texts. DefaultAssetsPickerTextDelegate()
filterOptions FilterOptionGroup? Allow users to customize assets filter options. null
specialItemBuilder WidgetBuilder? The widget builder for the special item. null
specialItemPosition SpecialItemPosition Allow users set a special item in the picker with several positions. SpecialItemPosition.none
allowSpecialItemWhenEmpty bool Whether the special item will display or not when assets is empty. false
routeCurve Curve The curve which the picker use to build page route transition. Curves.easeIn
routeDuration Duration The duration which the picker use to build page route transition. const Duration(milliseconds: 500)

Simple usage

final List<AssetEntity> assets = await AssetPicker.pickAssets(context);

or

AssetPicker.pickAsset(context).then((List<AssetEntity> assets) {
  /.../
});

Complete param usage

For various type of the picker, head over to the example and run it with no doubt.

Display selected assets

The AssetEntityImageProvider can display the thumb image of images & videos, and the original data of image. Use it like a common ImageProvider.

Image(image: AssetEntityImageProvider(asset, isOriginal: false))

Check the example for how it displays.

Register assets change observe callback

AssetPicker.registerObserve(); // Register callback.
AssetPicker.unregisterObserve(); // Unregister callback.

Customize with your own type or UI

AssetPickerBuilderDelegate, AssetPickerViewerBuilderDelegate, AssetPickerProvider and AssetPickerViewerProvider are all exposed and overridable. You can extend them and use your own type with generic type <A: Asset, P: Path>, then implement abstract methods. See the Custom page in the example which has an implementation based on <File, Directory> types.

Classes Introduction 💭

AssetEntity

/// Android: Database _id column
/// iOS    : `PhotoKit > PHObject > localIdentifier`
String id;

/// Android: `MediaStore.MediaColumns.DISPLAY_NAME`
/// iOS    : `PHAssetResource.filename`. Nullable
/// If you must need it, See [FilterOption.needTitle] or use [titleAsync].
String title;

/// Android: title
/// iOS    : [PHAsset valueForKey:@"filename"]
Future<String> get titleAsync;

/// * 1: [AssetType.image]
/// * 2: [AssetType.video]
/// * 3: [AssetType.audio]
/// * default: [AssetType.other]
AssetType get type;

/// Asset type int value.
int typeInt;

/// Duration of video, the unit is second.
/// If [type] is [AssetType.image], then it's value is 0.
/// See also: [videoDuration].
int duration;

/// Width of the asset.
int width;

/// Height of the asset.
int height;

/// Location information when shooting. Nullable.
/// When the device is Android 10 or above, it's ALWAYS null.
/// See also: [longitude].
double get latitude => _latitude ?? 0;
/// Also with a setter.

/// Get lat/lng from `MediaStore`(Android) / `Photos`(iOS).
/// In Android Q, this comes from EXIF.
Future<LatLng> latlngAsync();

/// Get [File] object.
/// Notice that this is not the origin file, so when it comes to some
/// scene like reading a GIF's file, please use `originFile`, or you'll
/// get a JPG.
Future<File> get file async;

/// Get the original [File] object.
Future<File> get originFile async;

/// The raw data for the entity, it may be large.
/// This property is NOT RECOMMENDED for video assets.
Future<Uint8List> get originBytes;

/// The thumbnail data for the entity. Usually use for displaying a thumbnail image widget.
Future<Uint8List> get thumbData;

/// Get thumbnail data with specific size.
Future<Uint8List> thumbDataWithSize(
  int width,
  int height, {
  ThumbFormat format = ThumbFormat.jpeg,
  int quality = 100,
});

/// Get the asset's size. Nullable if the manager is null,
Size get size;

/// If the asset is deleted, return false.
Future<bool> get exists => PhotoManager._assetExistsWithId(id);

/// The url is provided to some video player. Such as [flutter_ijkplayer](https://pub.dev/packages/flutter_ijkplayer)
///
/// Android: `content://media/external/video/media/894857`
/// iOS    : `file:///var/mobile/Media/DCIM/118APPLE/IMG_8371.MOV` in iOS.
Future<String> getMediaUrl();

/// Refresh the properties for the entity.
Future<AssetEntity> refreshProperties() async;

Frequently asked question ❔

Build failed with Unresolved reference: R

e: <path>\photo_manager-x.y.z\android\src\main\kotlin\top\kikt\imagescanner\core\PhotoManagerDeleteManager.kt: (116, 36): Unresolved reference: R
e: <path>\photo_manager-x.y.z\android\src\main\kotlin\top\kikt\imagescanner\core\PhotoManagerDeleteManager.kt: (119, 36): Unresolved reference: createTrashRequest
e: <path>\photo_manager-x.y.z\android\src\main\kotlin\top\kikt\imagescanner\core\PhotoManagerPlugin.kt: (341, 84): Unresolved reference: R
e: <path>\photo_manager-x.y.z\android\src\main\kotlin\top\kikt\imagescanner\core\utils\Android30DbUtils.kt: (34, 34): Unresolved reference: R
e: <path>\photo_manager-x.y.z\android\src\main\kotlin\top\kikt\imagescanner\core\utils\IDBUtils.kt: (27, 67): Unresolved reference: R

FAILURE: Build failed with an exception.

Run flutter clean first.

How can I get path from the AssetEntity to integrate with File object, upload or edit?

You don't need it (might be).

You can always request the File object with entity.originFile, if Uint8List then entity.originBytes.

If you still needs path after requested the File, get it through file.absolutePath.

How can I change the name of "Recent" or other entities name/properties?

The path entity called "Recent", brought by photo_manager in the path entities list, includes all AssetEntity on your device. "Recent" is a system named entity in most of platforms. While we provided ability to customize the text delegate, the name/properties can only be updated with SortPathDelegate . This is the only way that you have access to all path entities, or the only way that we exposed currently.

To change the name of the path entity, extend the SortPathDelegate with your own delegate, then write something like the code below:

/// Create your own sort path delegate.
class CustomSortPathDelegate extends SortPathDelegate {
  const CustomSortPathDelegate();

  @override
  void sort(List<AssetPathEntity> list) {
    ///...///

    // In here you can check every path entities if you want.
    // The only property we recommend to change is [name],
    // And we have no responsibility for issues caused by
    // other properties update.
    for (final AssetPathEntity entity in list) {
      // If the entity `isAll`, that's the "Recent" entity you want.
      if (entity.isAll) {
        entity.name = 'Whatever you want';
      }
    }

    ///...///
  }
}

Pass the delegate through the static call method, then you will get a self-named path entity.

Create AssetEntity from File or Uint8List (rawData)

In order to combine this package with camera shooting or something related, there's a solution about how to create an AssetEntity with File or Uint8List object.

final File file = your_file; // Your file object
final Uint8List byteData = await file.readAsBytes(); // Convert to Uint8List
final AssetEntity imageEntity = await PhotoManager.editor.saveImage(byteData); // Saved in the device then create an AssetEntity

If you don't want to keep the asset in your device, just delete it after you complete with your process (upload, editing, etc).

final List<String> result = await PhotoManager.editor.deleteWithIds([entity.id]);

ref: flutter_photo_manager#insert-new-item

Glide warning 'Failed to find GeneratedAppGlideModule'

W/Glide   (21133): Failed to find GeneratedAppGlideModule. You should include an annotationProcessor complie dependency on com.github.bumptech.glide:compiler in you application ana a @GlideModule annotated AppGlideModule implementation or LibraryGlideModules will be silently ignored.

Glide needs annotation to keep singleton, prevent conflict between instances and versions, so while the photo manager uses Glide to implement image features, the project which import this should define its own AppGlideModule. See Android section for implementation.

Disable ACCESS_MEDIA_LOCATION permission

Android contains ACCESS_MEDIA_LOCATION permission by default. This permission is introduced in Android Q. If your app doesn't need the permission, you need to add the following node to the AndroidManifest.xml in your app:

<uses-permission
  android:name="android.permission.ACCESS_MEDIA_LOCATION"
  tools:node="remove"
  />

Contributors ✨

Many thanks to these wonderful people (emoji key):


Alex Li

đŸ’ģ 🎨 📖 💡 🤔 🚧 đŸ’Ŧ 👀

Caijinglong

💡 🤔

Marcel Schneider

🐛 đŸ’ģ 🤔

ganlanshu0211

🐛 🤔

JasonHezz

🐛 đŸ’ģ

Yaniv Shaked

🌍 đŸ’ģ 🐛 🚧

avi-yadav

đŸ’ģ

Letalus

🐛 🌍

greymag

🌍

Nickolay Savchenko

🎨

Kosuke Saigusa

🌍

三é—ģäšĻåē—

📖

This project follows the all-contributors specification. Contributions of any kind welcomed!!

Acknowledgement

Every aspect of IntelliJ IDEA has been designed to maximize developer productivity. Together, intelligent coding assistance and ergonomic design make development not only productive but also enjoyable.

Thanks to JetBrains for allocating free open-source licenses for IDEs such as IntelliJ IDEA.