Flutter中FutureBuilder仅在递归调用完成后构建导致页面卡顿如何解决
问题原因
- 你当前使用的
listSync()是同步阻塞API,执行时会占用主UI线程,大量目录/文件遍历过程中UI无法响应,直接表现为卡顿 - FutureBuilder仅会在Future完全执行完成后触发一次构建,所以必须等所有图片路径扫描完毕才会渲染网格,无法增量加载
解决方案
1. 改造递归逻辑为异步流式输出
使用Dart的Stream每找到一张图片就向外输出一次路径,同时将同步遍历改为异步遍历,避免阻塞UI线程:
Directory dir = Directory("/storage/emulated/0"); late CameraController controller; List<String> visited = []; List<String> files = []; Stream<String> getPhotos(Directory olddir) async* { // 异步遍历目录,不会阻塞UI线程 await for (var item in olddir.list()) { // 直接用is判断类型,比对比runtimeType字符串效率更高更准确 if (item is Directory && !visited.contains(item.path) && !item.path.contains(".thumbnail")) { visited.add(item.path); // 递归遍历子目录,向下传递流输出 yield* getPhotos(item); } else if (item is File && item.path.endsWith(".jpg")) { files.add(item.path); // 每找到一张图片就触发一次流输出,通知UI更新 yield item.path; } } }
2. 用StreamBuilder替代FutureBuilder做增量渲染
StreamBuilder可以监听流的每一次输出,每新增一张图片路径就触发一次UI刷新,无需等待全部扫描完成即可逐步渲染图片:
StreamBuilder<String>( stream: getPhotos(dir), builder: (context, snapshot) { return GridView.builder( scrollDirection: Axis.horizontal, shrinkWrap: true, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 5 ), itemCount: files.isNotEmpty ? files.length : 5, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.all(3.0), child: index >= files.length ? Image.asset("assets/images/load.gif") : Image.file( File(files[index]), fit: BoxFit.cover, filterQuality: FilterQuality.low, width: 200, height: 200, ), ); } ); } )
额外优化建议
- 如果设备内图片数量过万,可以将扫描逻辑放到
compute中运行在后台隔离线程,完全不占用UI线程资源 - 可以添加批量更新逻辑,比如每找到10张图片再触发一次UI刷新,避免短时间内频繁重建导致性能损耗
- 权限申请要提前做好,Android13以上需要申请图片读写权限才能正常扫描公共存储目录
内容的提问来源于stack exchange,提问作者nik nikhil
相关产品推荐
相关产品推荐

