Flutter应用列出PDF文件时运行过慢问题求助
Flutter 设备存储PDF文件列表加载过慢的优化方案
问题背景
我在Flutter应用中实现从设备存储列出PDF文件的功能,但当前加载速度过慢。以下是我的实现代码:
获取文件的函数
var files; Future<void> getFiles() async { if (await Permission.storage.request().isGranted) { // 权限已获取 } List<StorageInfo> storageInfo = await PathProviderEx.getStorageInfo(); var root = storageInfo[0].rootDir; var fm = FileManager(root: Directory(root)); files = await fm.dirsTree( excludedPaths: ["/storage/emulated/0/Android"], // 未过滤PDF文件 ); setState(() {}); } @override void initState() { getFiles(); super.initState(); }
页面Body代码
body: files == null ? Text("Searching Files") : ListView.builder( itemCount: files?.length ?? 0, itemBuilder: (context, index) { return Card( child: ListTile( title: Text(files[index].path.split('/').last), onTap: (){} ) ); } )
优化方案
1. 精准过滤PDF文件,避免扫描所有文件
原代码使用dirsTree会遍历所有目录和文件,且未过滤扩展名。改用filesTree并指定仅扫描.pdf文件,大幅减少扫描量:
files = await fm.filesTree( excludedPaths: ["/storage/emulated/0/Android"], extensions: ["pdf"], // 仅扫描PDF文件 );
2. 用后台线程执行扫描任务,避免阻塞UI
使用Flutter的compute函数将扫描逻辑移至后台隔离线程,防止UI卡顿:
// 定义单独的扫描函数,供compute调用 Future<List<File>> scanPdfFiles(String rootDir) async { var fm = FileManager(root: Directory(rootDir)); return await fm.filesTree( excludedPaths: ["/storage/emulated/0/Android"], extensions: ["pdf"], ); } // 修改getFiles函数 Future<void> getFiles() async { if (await Permission.storage.request().isGranted) { List<StorageInfo> storageInfo = await PathProviderEx.getStorageInfo(); var root = storageInfo[0].rootDir; // 用compute在后台执行扫描 files = await compute(scanPdfFiles, root); setState(() {}); } }
3. 扩展排除目录范围,减少扫描路径
添加更多无需扫描的系统/缓存目录,进一步缩小扫描范围:
excludedPaths: [ "/storage/emulated/0/Android", "/storage/emulated/0/Download/.cache", "/storage/emulated/0/DCIM/.thumbnails", "/storage/emulated/0/tencent", "/storage/emulated/0/com.whatsapp", ],
4. 使用FutureBuilder替代手动setState,优化UI渲染
避免在initState中直接调用扫描并手动setState,改用FutureBuilder自动管理异步状态,提升UI流畅度:
// 移除initState中的getFiles调用,将getFiles改为返回Future Future<List<File>> getFiles() async { if (await Permission.storage.request().isGranted) { List<StorageInfo> storageInfo = await PathProviderEx.getStorageInfo(); var root = storageInfo[0].rootDir; return await compute(scanPdfFiles, root); } return []; } // 修改body代码 body: FutureBuilder<List<File>>( future: getFiles(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: Text("Searching Files")); } else if (snapshot.hasError) { return const Center(child: Text("Failed to load files")); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text("No PDF files found")); } else { return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, index) { final file = snapshot.data![index]; return Card( child: ListTile( title: Text(file.path.split('/').last), onTap: () {}, ), ); }, ); } }, ),
5. 缓存扫描结果,避免重复扫描
将扫描到的文件路径缓存到本地,下次启动时先读取缓存,再进行增量扫描:
// 示例:用shared_preferences缓存 import 'package:shared_preferences/shared_preferences.dart'; Future<void> saveFilesToCache(List<File> files) async { final prefs = await SharedPreferences.getInstance(); final paths = files.map((f) => f.path).toList(); await prefs.setStringList('pdf_files', paths); } Future<List<File>> loadFilesFromCache() async { final prefs = await SharedPreferences.getInstance(); final paths = prefs.getStringList('pdf_files'); if (paths != null) { return paths.map((p) => File(p)).where((f) => f.existsSync()).toList(); } return []; } // 在getFiles中先读缓存,再扫描更新 Future<List<File>> getFiles() async { if (await Permission.storage.request().isGranted) { // 先加载缓存 var cachedFiles = await loadFilesFromCache(); // 扫描新文件 List<StorageInfo> storageInfo = await PathProviderEx.getStorageInfo(); var root = storageInfo[0].rootDir; var scannedFiles = await compute(scanPdfFiles, root); // 更新缓存 await saveFilesToCache(scannedFiles); return scannedFiles; } return []; }
内容的提问来源于stack exchange,提问作者Faizan Abbas Bangash
相关产品推荐
相关产品推荐

