Flutter如何快速加载相册全部图片,实现流畅的自定义图片选择器
底部弹窗图片选择器流畅度优化方案
1. 优先加载适配尺寸的缩略图,禁止直接加载全分辨率原图
全分辨率原图单张大小可达数MB,是加载卡顿的核心原因,直接替换为缩略图加载即可获得数倍性能提升:
- 用
requestThumbnail方法替代直接读取asset.file,根据列表项的实际显示尺寸传入略大于实际大小的宽高(适配多倍屏),压缩质量设置为70-90区间即可兼顾清晰度和体积 - 给
Image.memory加上cacheWidth和cacheHeight参数,强制把图片缓存到指定尺寸,减少内存占用
示例代码:
// 错误写法:加载全分辨率原图 Image.file(await asset.file); // 正确写法:加载适配列表尺寸的缩略图 final Uint8List? thumbData = await asset.requestThumbnail( width: 200, // 假设列表项显示尺寸为100*100,传2倍尺寸适配2K屏 height: 200, quality: 80, ); if (thumbData != null) { Image.memory( thumbData, cacheWidth: 200, cacheHeight: 200, fit: BoxFit.cover, ); }
2. 优化缓存策略
- 调整图片缓存上限,给图片选择器场景单独分配更大的缓存空间,避免滚动时已加载的缩略图被频繁回收重建:
// 进入图片选择器前设置缓存上限为100MB,单位为字节 PaintingBinding.instance.imageCache.maximumSizeBytes = 100 * 1024 * 1024; // 关闭选择器后可以恢复默认值,避免占用过多内存
- 已加载过的缩略图数据可以存在内存Map中,用asset的唯一id做key,重复进入选择器时无需重新生成缩略图
3. 列表懒加载与性能优化
- 必须用
GridView.builder/ListView.builder构建图片列表,仅渲染当前视口内的元素,禁止一次性构建所有图片项 - 分页查询相册数据,每次仅加载20-30张图片,滚动到接近底部时再加载下一页,避免一次性查询数千条相册数据占用大量内存
- 给每个图片项添加
ValueKey(asset.id),避免列表重绘时元素错位和不必要的重建 - 开启
GridView的addAutomaticKeepAlives: true配置,让已加载的图片项保持存活状态,不会滚出视口就被销毁
4. 滚动过程的加载限流
快速滚动时会触发大量并发的缩略图加载请求,阻塞UI线程导致卡顿,通过监听滚动状态做防抖处理:
bool _isFastScrolling = false; NotificationListener<ScrollNotification>( onNotification: (notification) { if (notification is ScrollUpdateNotification) { // 滚动速度超过阈值时标记为快速滚动,暂停非视口内的图片加载 _isFastScrolling = (notification.scrollDelta?.abs() ?? 0) > 20; } else if (notification is ScrollEndNotification) { _isFastScrolling = false; // 滚动结束后触发当前视口内的图片加载 } return false; }, child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, childAspectRatio: 1, ), itemBuilder: (context, index) { // 快速滚动时先显示占位图,滚动停止后再加载缩略图 if (_isFastScrolling) { return Container(color: Colors.grey[200]); } // 正常加载缩略图逻辑 return YourImageItem(asset: assetList[index]); }, itemCount: assetList.length, ), )
完成以上优化后即可达到和Telegram图片选择器相近的滚动流畅度。
内容的提问来源于stack exchange,提问作者Mahdi Dahouei
相关产品推荐
相关产品推荐

