Flutter加载本地多图GridView滑动内存溢出崩溃如何解决
问题描述
我尝试使用local_image_provider插件获取安卓设备上的图片与视频地址,获取到地址后将其添加到列表中,实现代码如下:
List<DeviceImage> devideImgs = []; getImage() async { lip.LocalImageProvider imageProvider = lip.LocalImageProvider(); bool hasPermission = await imageProvider.initialize(); if (hasPermission) { List<lip.LocalImage> images = await imageProvider.findLatest(100000); images.forEach((image) async { if (image.isImage) { devideImgs.add(DeviceImage(image)); } }); } else { print("The user has denied access to images on their device."); } update(); }
随后我尝试通过如下代码在网格视图中展示这些图片:
StaggeredGridView.countBuilder( crossAxisCount: 3, mainAxisSpacing: 3, crossAxisSpacing: 3, itemCount: devideImgs.length, itemBuilder: (context, index) { print('item $index created in grid view'); key: UniqueKey(); return Item(devideImgs[index]); }, staggeredTileBuilder: (index) => StaggeredTile.count(1,1), )
以下是我自定义的**Item**组件实现代码:
Stack( children: [ VisibilityDetector( key: UniqueKey(), onVisibilityChanged: (visibilityInfo) async { }, child: Container( child: GestureDetector( onTap: () {}, child: SizedBox( width: double.maxFinite, height: double.maxFinite, child: Image( image: widget.image, fit: BoxFit.cover, )) ))), ], );
上述代码中widget.image为DeviceImage类型,功能运行初期表现正常,但滑动网格视图时出现内存被占满的问题,抛出如下错误日志:
I/art (19971): Starting a blocking GC Alloc I/art (19971): Alloc sticky concurrent mark sweep GC freed 87(4KB) AllocSpace objects, 0(0B) LOS objects, 15% free, 81MB/97MB, paused 1.224ms total 14.060ms
后续应用直接崩溃。我已经使用了GridView.builder,但推测Item组件及对应图片在滑出屏幕可视区域后未被内存回收,请问该如何修复该问题?
解决方案
这个内存崩溃问题不是组件未回收导致的,核心问题有3个,按优先级修复即可:
- 不要一次性加载全量图片元数据:你调用
findLatest(100000)直接拉取设备上最多10万张图片的元数据,列表初始化阶段就会占用大量不必要的内存。改成分页加载逻辑,初始只拉取前30-50张,滑动到列表接近底部时再加载下一批数据即可。 - 必须给图片设置缓存尺寸:直接加载原图到内存是OOM的核心原因,单张高清原图动辄占用几MB到十几MB内存,网格中同时加载十几张就会占满系统分配给应用的内存阈值。给
Image组件手动传入cacheWidth、cacheHeight参数,按照网格单元的实际像素尺寸加载缩略图即可,比如3列布局、1080p屏幕下,单格宽度为360px,设置cacheWidth: 360就能保证显示清晰度,内存占用可以降到原图的1/10甚至更低。 - 停止滥用
UniqueKey:你在itemBuilder和VisibilityDetector中每次构建都新建UniqueKey(),会导致Flutter无法正确复用已销毁的元素和图片缓存,滑动时会反复重新加载图片、重建组件状态,进一步加剧内存占用。给每个Item和VisibilityDetector传入固定的ValueKey即可,值用图片的唯一标识(比如图片id、存储路径),不要在build方法里动态生成新的唯一键。
额外优化:可以给Image组件设置gaplessPlayback: true,避免滑动时图片重绘闪烁;如果图片量级很大,建议使用专门的列表图片加载方案,内置的内存缓存管理会自动回收不可见区域的图片占用内存。
注意:你代码里itemBuilder中写的
key: UniqueKey();属于无效死代码,既没有作为参数传给Item组件,还会在每次构建时无意义生成新对象,直接删掉即可。
内容的提问来源于stack exchange,提问作者mohammad reza shabani
相关产品推荐
相关产品推荐

