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

在ListView.builder中多选文件后实现复制、建文件夹及压缩功能

完整实现方案

以下是基于Flutter的完整实现代码,涵盖文件选中、复制、新建文件夹及压缩功能:

依赖准备

在pubspec.yaml中添加必要依赖:

dependencies:
  flutter:
    sdk: flutter
  path: ^1.8.3
  path_provider: ^2.1.1
  archive: ^3.4.10

完整代码

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import 'package:archive/archive.dart';
import 'package:path_provider/path_provider.dart';

class FileManagerScreen extends StatefulWidget {
  final List<File> files; // 传入你的文件列表

  const FileManagerScreen({super.key, required this.files});

  @override
  State<FileManagerScreen> createState() => _FileManagerScreenState();
}

class _FileManagerScreenState extends State<FileManagerScreen> {
  final Set<File> _selectedFiles = {};

  // 切换文件选中状态
  void _toggleFileSelection(File file) {
    setState(() {
      _selectedFiles.contains(file) 
          ? _selectedFiles.remove(file) 
          : _selectedFiles.add(file);
    });
  }

  // 复制选中文件到临时目录
  Future<List<File>> _copySelectedFiles() async {
    final tempDir = await getTemporaryDirectory();
    final copiedFiles = <File>[];

    for (final file in _selectedFiles) {
      final destPath = path.join(tempDir.path, path.basename(file.path));
      final copiedFile = await file.copy(destPath);
      copiedFiles.add(copiedFile);
    }

    return copiedFiles;
  }

  // 创建新文件夹
  Future<Directory?> _createNewFolder(String folderName) async {
    final appDocDir = await getApplicationDocumentsDirectory();
    final newFolderDir = Directory(path.join(appDocDir.path, folderName));

    if (!await newFolderDir.exists()) {
      await newFolderDir.create(recursive: true);
      return newFolderDir;
    } else {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('文件夹 "$folderName" 已存在')),
        );
      }
      return null;
    }
  }

  // 压缩文件到指定文件夹
  Future<void> _compressFilesToFolder(List<File> files, Directory targetFolder) async {
    final archive = Archive();

    // 添加文件到压缩包
    for (final file in files) {
      final fileBytes = await file.readAsBytes();
      archive.addFile(ArchiveFile(
        path.basename(file.path),
        fileBytes.length,
        fileBytes,
      ));
    }

    // 生成并保存ZIP文件
    final zipBytes = ZipEncoder().encode(archive);
    if (zipBytes == null) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('压缩失败,请重试')),
        );
      }
      return;
    }

    final zipPath = path.join(targetFolder.path, 'compressed_files.zip');
    await File(zipPath).writeAsBytes(zipBytes);

    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('文件已压缩至:${targetFolder.path}')),
      );
    }
  }

  // 执行完整流程:复制 -> 新建文件夹 -> 压缩
  Future<void> _handleCopyAndCompress() async {
    if (_selectedFiles.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('请先选中至少一个文件')),
      );
      return;
    }

    // 1. 复制选中文件到临时目录
    final copiedFiles = await _copySelectedFiles();

    // 2. 弹出输入框获取文件夹名称
    final folderName = await showDialog<String>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('新建文件夹'),
        content: TextField(
          decoration: const InputDecoration(hintText: '输入文件夹名称'),
          autofocus: true,
          onSubmitted: (value) => Navigator.pop(context, value.trim()),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () {
              final controller = (context.widget as AlertDialog).content as TextField;
              Navigator.pop(context, controller.text.trim());
            },
            child: const Text('创建'),
          ),
        ],
      ),
    );

    if (folderName == null || folderName.isEmpty) return;

    // 3. 创建目标文件夹
    final targetFolder = await _createNewFolder(folderName);
    if (targetFolder == null) return;

    // 4. 压缩文件到目标文件夹
    await _compressFilesToFolder(copiedFiles, targetFolder);

    // 清理临时文件
    for (final file in copiedFiles) {
      await file.delete();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('文件管理器'),
        actions: [
          if (_selectedFiles.isNotEmpty)
            TextButton(
              onPressed: _handleCopyAndCompress,
              child: const Text('复制并压缩', style: TextStyle(color: Colors.white)),
            ),
        ],
      ),
      body: ListView.builder(
        itemCount: widget.files.length,
        itemBuilder: (context, index) {
          final file = widget.files[index];
          final isSelected = _selectedFiles.contains(file);

          return ListTile(
            leading: const Icon(Icons.file_copy),
            title: Text(path.basename(file.path)),
            trailing: isSelected ? const Icon(Icons.check_circle, color: Colors.blue) : null,
            onTap: () => _toggleFileSelection(file),
            onLongPress: () => _toggleFileSelection(file),
          );
        },
      ),
    );
  }
}

关键功能说明

  1. 选中状态管理:用Set<File>存储选中文件,避免重复选中,点击/长按ListTile切换选中状态。
  2. 文件复制:将选中文件复制到系统临时目录,避免操作原文件,压缩完成后自动清理临时文件。
  3. 新建文件夹:通过弹窗输入文件夹名称,在应用文档目录下创建文件夹,若已存在则提示用户。
  4. 文件压缩:使用archive包将文件打包为ZIP,保存到新建的文件夹中,压缩完成后给出成功提示。

额外提示

  • 若需操作外部存储,需添加对应权限:Android需申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE,iOS需在Info.plist中添加NSFileProviderDomainUsageDescription权限描述,可配合permission_handler包处理权限请求。
  • 可根据需求扩展错误处理逻辑,例如捕获文件复制、压缩过程中的异常并给出对应提示。

内容的提问来源于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.11 13:01:02