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
相关产品推荐
相关产品推荐

