媒体选择器:wechat_assets_picker

基本介绍

wechat_assets_picker 是 Flutter 社区一个很常用的 媒体选择器库,灵感来自 微信的图片/视频选择界面。它主要功能是 选择图片、视频、音频 等多媒体文件,并且支持 相册浏览相机拍摄多选裁剪过滤 等功能。

它的底层基于 photo_manager 获取本地媒体资源,然后通过 类似微信风格的 UI 进行选择。

核心功能如下:

  1. 支持多类型媒体选择
    • 图片(JPEG、PNG、GIF、WebP 等)
    • 视频(mp4、mov 等,支持预览播放)
    • 音频(可选,依赖系统支持)
  2. 选择模式
    • 单选 / 多选
    • 预设已选资源(编辑已选)
    • 最大/最小选择数量限制
  3. UI 高度自定义
    • 类似微信的相册风格
    • 可以自定义主题、文案、图标
    • 支持国际化

入门案例

安装

flutter pub add wechat_assets_picke

配置

结合 permission_handler 配置

Android

AndroidManifest.xml(部分版本需要按需添加):

<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 以下,还可能需要:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="29" />

iOS

ios/Runner/Info.plist

<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册来选择图片/视频</string>

ios/Podfile

## dart: PermissionGroup.photos
'PERMISSION_PHOTOS=1',

代码

import 'package:flutter/material.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Wechat Picker Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const WechatPickerDemo(),
    );
  }
}

class WechatPickerDemo extends StatefulWidget {
  const WechatPickerDemo({super.key});

  @override
  State<WechatPickerDemo> createState() => _WechatPickerDemoState();
}

class _WechatPickerDemoState extends State<WechatPickerDemo> {
  List<AssetEntity>? selectedAssets; // 保存选择结果

  /// 检查并请求权限
  Future<bool> _requestPermissions() async {
    // 请求相册权限
    final photoStatus = await Permission.photos.request();
    // 请求相机权限(如果需要用相机入口)
    // final cameraStatus = await Permission.camera.request();

    if (photoStatus.isGranted) {
      return true;
    } else {
      // 引导用户去设置界面
      openAppSettings();
      return false;
    }
  }

  /// 打开选择器
  Future<void> _pickAssets() async {
    // 检查权限:同时获取相机和相册的权限
    final hasPermission = await _requestPermissions();
    if (!hasPermission) return;

    final result = await AssetPicker.pickAssets(
      context,
      pickerConfig: const AssetPickerConfig(
        requestType: RequestType.image, // 只选图片
        maxAssets: 5, // 最多 5 张
      ),
    );

    if (result != null) {
      // 更新状态以显示选择结果
      setState(() => selectedAssets = result);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Wechat Assets Picker + Permission")),
      body: Center(
        child: Column(
          children: [
            // 按钮
            ElevatedButton(onPressed: _pickAssets, child: const Text("选择图片")),
            // 如果选择了图片
            if (selectedAssets != null)
              Wrap(
                children: selectedAssets!.map((e) {
                  return Padding(
                    padding: const EdgeInsets.all(4.0),
                    child: AssetEntityImage(
                      e,
                      isOriginal: false,
                      thumbnailSize: const ThumbnailSize.square(200),
                      width: 100,
                      height: 100,
                      fit: BoxFit.cover,
                    ),
                  );
                }).toList(),
              ),
          ],
        ),
      ),
    );
  }
}

AssetEntityImage

是什么

AssetEntityImage 组件是 wechat_assets_picker中专门用来展示 AssetEntity(相册里的图片/视频缩略图) 的小工具。它的作用有点像 Image.fileImage.memory,但帮你把底层的异步加载和缩略图获取都封装好了。

  • AssetEntitywechat_assets_picker/photo_manager 提供的 媒体资源对象,里面包含了图片、视频、音频等文件的元信息(id、路径、类型、宽高等)。
  • AssetEntityImage:基于 AssetEntity 的封装,用来直接显示缩略图或原图的 Widget

构造函数的参数

AssetEntityImage(
  AssetEntity entity, {
  bool isOriginal = false,              // 是否加载原图
  ThumbnailSize? thumbnailSize,         // 缩略图大小
  double? width,                        // 宽度
  double? height,                       // 高度
  BoxFit fit = BoxFit.cover,            // 填充模式
  Alignment alignment = Alignment.center,
  Color? color,                         // 前景色 (比如滤镜)
  BlendMode? colorBlendMode,            // 前景色混合模式
  WidgetBuilder? errorBuilder,          // 加载失败时展示的 widget
  WidgetBuilder? loadingBuilder,        // 加载中时展示的 widget
})
  • entity:你传入的 AssetEntity 对象,比如相册里的一张图或一个视频。

  • isOriginal

    • false(默认):显示缩略图,加载速度快,适合列表、预览用。

    • true:显示原始图像(体积大,耗时、耗内存),一般用在全屏查看时。

  • thumbnailSize控制缩略图大小,比如:

    thumbnailSize: const ThumbnailSize.square(200),
    

    表示生成 200x200 的正方形缩略图。

  • widthheightWidget 的显示尺寸(和 thumbnailSize 不同,这里是 Flutter UI 层的大小控制)。

  • fit:类似于 ImageBoxFit,比如:

    • BoxFit.cover → 填满裁剪

    • BoxFit.contain → 保持比例完整显示

  • errorBuilder / loadingBuilder:自定义加载失败或加载中时的 UI,比如显示一个占位符。

使用示例

显示缩略图(常用)

AssetEntityImage(
  asset,  // AssetEntity
  thumbnailSize: const ThumbnailSize.square(200),
  width: 100,
  height: 100,
  fit: BoxFit.cover,
)
  • thumbnailSize(缩略图像素大小):控制从系统相册/媒体库里请求出来的 图片数据的分辨率。比如:ThumbnailSize.square(200) → 系统返回 200×200 像素 的图片数据(内存里的 Bitmap)。可以理解为“下载的图有多大”。

  • widthheight(Widget 大小):控制在 Flutter UI 中显示的 Widget 尺寸。比如:width: 100, height: 100 → 在屏幕上只占 100×100 的空间。可以理解为“显示的时候放多大”。

  • 上面配置的效果:Flutter 会请求 200×200 的图像数据(质量更好,不容易糊)。但是在界面上用 100×100 的空间显示(等于把图缩小)。

  • 为什么不直接用 100×100?

    如果 thumbnailSize 设太小(比如 100×100),当你在列表里滚动时可能没问题,但放大或在高分屏设备上看,就容易模糊。

    一般做法:thumbnailSize 设比 Widget 显示尺寸稍大(比如 2×,200px 用来显示 100px),这样在 Retina 屏上也清晰。

显示原图(少用,适合预览大图)

AssetEntityImage(
  asset,
  isOriginal: true,  // 原图模式
  width: double.infinity,
  height: double.infinity,
  fit: BoxFit.contain,
)

带加载/失败占位

AssetEntityImage(
  asset,
  width: 100,
  height: 100,
  fit: BoxFit.cover,
  loadingBuilder: (_) => const Center(child: CircularProgressIndicator()),
  errorBuilder: (_) => const Icon(Icons.error, color: Colors.red),
)

AssetPicker

是什么

  • AssetPickerwechat_assets_picker 库里 调用资源选择器 UI 的静态方法集合
  • 简单说:它帮你弹出一个微信风格的 相册选择器界面,并在用户选择后返回一组 AssetEntity(图片/视频等媒体资源)。
  • 你不需要自己写 UI,只要调用 AssetPicker.pickAssets(...),就能获得用户选的资源。
  • AssetPicker 会自动访问系统相册里的图片/视频文件夹(相册目录 / Albums)
    • wechat_assets_picker 的底层依赖 photo_manager 库。
    • photo_manager 会调用 Android MediaStore / iOS PhotoKit,去获取系统相册里所有的 相册文件夹(相册集 / album)

核心方法

pickAssets

最常用的方法:选择图片/视频/音频。

final List<AssetEntity>? result = await AssetPicker.pickAssets(
  context,
  pickerConfig: const AssetPickerConfig(
    requestType: RequestType.image, // 选择类型:图片/视频/音频/混合
    maxAssets: 9,                   // 最大选择数量
  ),
);
  • 返回值List<AssetEntity>?,表示用户选择的媒体资源列表(图片、视频等)。如果用户取消选择则返回 null

pickAssetsWithDelegate

高级用法,允许你传入自定义的 delegate(委托类),完全控制 UI 和交互逻辑。

final result = await AssetPicker.pickAssetsWithDelegate(
  context,
  delegate: MyCustomPickerDelegate(),
);

一般只在你要 自定义 UI深度改造选择器 时用。

配置类:AssetPickerConfig

这是 AssetPicker 的核心参数配置,决定选择器的行为。常用参数有:

AssetPickerConfig({
  this.requestType = RequestType.image, // 选择类型
  this.maxAssets = 9,                   // 最大数量
  this.selectedAssets,                  // 已选中的资源(编辑模式)
  this.themeColor,                      // 主题色
  this.gridCount = 4,                   // 网格列数
  this.pageSize = 80,                   // 分页加载数量
  this.textDelegate,                    // 国际化文案
  this.specialPickerType,               // 特殊模式,比如朋友圈
  this.filterOptions,                   // 资源过滤,比如只显示最近3天
})

RequestType

RequestType 的可选值有如下几种:

RequestType.image      // 只获取图片
RequestType.video      // 只获取视频
RequestType.audio      // 只获取音频
RequestType.common     // 图片 + 视频
RequestType.all        // 图片 + 视频 + 音频
  • audio:仅返回音频文件(录音、音乐等,前提是平台支持)。iOS 上可能受限,因为 iOS 的 PhotoKit 不总是允许访问音频。

SpecialPickerType


/// Provide some special picker types to integrate
/// un-common pick pattern.
/// 提供一些特殊的选择器类型以整合非常规的选择行为。
enum SpecialPickerType {
  /// WeChat Moments mode.
  /// 微信朋友圈模式
  ///
  /// The user can only select *one video* or *multiple images* at the same time,
  /// and those two asset types cannot be selected at the same time.
  /// 用户只可以选择 **一个视频** 或 **多个图片**,并且两种类型互斥。
  wechatMoment,

  /// Disable preview of assets.
  /// 禁用资源预览
  ///
  /// There is no preview mode when clicking grid items.
  /// In multiple select mode, any click (either on the select indicator or on
  /// the asset itself) will select the asset.
  /// In single select mode, any click directly selects the asset and returns.
  /// 用户在点击网格的 item 时无法进入预览。
  /// 在多选模式下无论点击选择指示还是 item 都将触发选择,
  /// 而在单选模式下将直接返回点击的资源。
  noPreview,
}

AssetEntity

AssetEntity 表示相册中一条资源的元信息 + 获取资源的方法。比如:一张图片,一段视频,一条音频

下面是 AssetEntity 的一些常见属性:

属性 类型 说明
id String 系统中该资源的唯一标识
type AssetType 资源类型(图片/视频/音频/其它)
width / height int 媒体的原始像素宽高
duration int 视频或音频的时长(秒),图片为 0
createDateTime DateTime 创建时间
modifiedDateTime DateTime 修改时间
isFavorite bool 是否收藏(仅部分平台支持)
relativePath String? 文件在系统相册中的相对路径(Android 常见,iOS 受限制)
title String? 文件名(部分平台支持)

常用方法:

方法 返回 说明
thumbnailData Uint8List? 获取缩略图数据(图片字节)
thumbnailDataWithSize(ThumbnailSize) Uint8List? 获取指定大小的缩略图
file Future<File?> 获取原始文件
originBytes Future<Uint8List?> 获取原始图像的二进制数据
latlngAsync() Future<LatLng?> 获取位置信息(若有 GPS 信息)

×

喜欢就点赞,疼爱就打赏