You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter相册视频列表分页优化:解决加载慢与导航延迟问题

优化方案:相机视频列表加载与缩略图性能提升

1. 异步化视频列表加载,避免阻塞页面导航

把initState里的同步listSync改成后台异步操作,避免卡住UI线程导致页面跳转延迟。用compute将IO操作隔离到单独Isolate,页面先渲染加载状态,后台并行获取视频列表:

@override
void initState() {
  super.initState();
  _loadVideos();
}

Future<void> _loadVideos() async {
  final files = await compute(_filterCameraVideos, '/storage/emulated/0/DCIM/Camera');
  setState(() {
    _allVideos = files;
    _loadNextPage();
  });
}

static List<File> _filterCameraVideos(String path) {
  final dir = Directory(path);
  // 只保留视频格式文件,减少无效处理
  return dir.listSync().where((entity) {
    if (entity is File) {
      final ext = entity.path.toLowerCase();
      return ext.endsWith('.mp4') || ext.endsWith('.mov') || ext.endsWith('.avi');
    }
    return false;
  }).cast<File>().toList();
}

toList()本身耗时极低,主要开销在listSync和文件过滤,提前过滤视频格式能大幅减少后续处理的文件数量。

2. 分页加载,降低初始渲染压力

实现分页逻辑,每次加载固定数量的视频,滚动到底部时再加载下一页,避免一次性处理所有视频:

int _currentPage = 0;
final int _pageSize = 20;
List<File> _allVideos = [];
List<File> _loadedVideos = [];
bool _isLoading = false;

void _loadNextPage() {
  if (_isLoading || _currentPage * _pageSize >= _allVideos.length) return;
  _isLoading = true;
  final start = _currentPage * _pageSize;
  final end = start + _pageSize > _allVideos.length ? _allVideos.length : start + _pageSize;
  final newVideos = _allVideos.sublist(start, end);
  setState(() {
    _loadedVideos.addAll(newVideos);
    _currentPage++;
    _isLoading = false;
  });
}

// 监听滚动触发下一页加载
@override
Widget build(BuildContext context) {
  return NotificationListener<ScrollNotification>(
    onNotification: (notification) {
      if (notification is ScrollEndNotification && !_isLoading) {
        final metrics = notification.metrics;
        if (metrics.pixels >= metrics.maxScrollExtent - 200) {
          _loadNextPage();
        }
      }
      return true;
    },
    child: SingleChildScrollView(
      child: Column(
        children: [
          for (var video in _loadedVideos) VideoThumbnailWidget(file: video),
          if (_isLoading) const CircularProgressIndicator(),
        ],
      ),
    ),
  );
}

3. 缩略图异步生成+缓存,解决加载缓慢问题

用video_thumbnail插件异步生成缩略图,同时加入内存缓存避免重复生成,减少滚动时的重复计算:

// 异步生成缩略图
Future<Uint8List?> _generateThumbnail(File file) async {
  return VideoThumbnail.thumbnailData(
    video: file.path,
    imageFormat: ImageFormat.JPEG,
    maxWidth: 150, // 缩小尺寸降低生成耗时与内存占用
    quality: 75,
  );
}

// 带缓存的缩略图组件
class VideoThumbnailWidget extends StatelessWidget {
  final File file;
  static final Map<String, Uint8List?> _thumbnailCache = {};

  const VideoThumbnailWidget({super.key, required this.file});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Uint8List?>(
      future: _thumbnailCache.containsKey(file.path) 
          ? Future.value(_thumbnailCache[file.path]) 
          : _generateThumbnail(file).then((data) {
              _thumbnailCache[file.path] = data;
              return data;
            }),
      builder: (context, snapshot) {
        if (snapshot.hasData && snapshot.data != null) {
          return Image.memory(snapshot.data!, fit: BoxFit.cover);
        }
        return const Placeholder(); // 加载占位符
      },
    );
  }
}

4. 替换Column+for:用ListView.builder优化渲染性能

Column+for会一次性渲染所有组件,即使不在屏幕内,内存开销极大。改用ListView.builder懒加载,只渲染可见区域的组件,配合缩略图缓存完全可以解决之前GridView的重建问题:

@override
Widget build(BuildContext context) {
  return ListView.builder(
    itemCount: _loadedVideos.length + (_isLoading ? 1 : 0),
    itemBuilder: (context, index) {
      if (index == _loadedVideos.length) {
        return const Center(child: CircularProgressIndicator());
      }
      return VideoThumbnailWidget(file: _loadedVideos[index]);
    },
  );
}

内容的提问来源于stack exchange,提问作者Shreyansh Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 10:52:22