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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 11:55:26