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

Flutter使用Bloc架构时如何取消Firebase Storage文件下载任务

Android Flutter Bloc 实现文件下载取消功能方案

1. 新增取消下载事件

首先在FileEvent的定义中新增取消事件,用于UI层向Bloc发送取消操作指令:

// 放在原有FileEvent的定义文件中
class CancelDownloadEvent extends FileEvent {
  @override
  List<Object?> get props => [];
}

2. 注册事件并修改Bloc核心逻辑

2.1 注册事件处理器

在FileBloc的构造函数中注册新增的取消事件处理方法:

class FileBloc extends Bloc<FileEvent, FileState> {
  FileBloc() : super(FileState.initial()) {
    on<SaveFileEvent>(_saveFile); // 原有下载事件注册
    on<CancelDownloadEvent>(_cancelDownload); // 新增取消事件注册
  }

  // 后续处理方法写在类内部
}

2.2 实现取消下载处理方法

在Bloc中实现_cancelDownload方法,调用存储任务的cancel接口,清理临时文件并重置状态:

Future<void> _cancelDownload(Emitter<FileState> emit) async {
  final activeTask = state.task;
  if (activeTask != null) {
    try {
      await activeTask.cancel();
    } catch (_) {
      // 取消抛出的异常无需特殊处理,直接走后续状态重置即可
    }
  }
  // 重置为初始未下载状态
  emit(const FileState(
    status: BlocStatus.initial,
    exists: false,
    task: null,
  ));
}

2.3 修复原有下载方法的逻辑问题

原有saveFile方法存在逻辑错误:创建下载任务后直接标记为loaded状态,没有等待任务完成、也没有处理异常和取消场景。修改后的完整下载逻辑如下:

Future<void> _saveFile(Emitter<FileState> emit, SaveFileEvent event) async {
  emit(_loadingState());
  final dir = await getApplicationDocumentsDirectory();
  final targetFile = File('${dir.path}/${event.fileToDownload.name}');
  
  // 提前删除残留的不完整文件,避免影响本次下载
  if (await targetFile.exists()) {
    await targetFile.delete();
  }

  final storageRef = FirebaseStorage.instance.ref('uploads/${event.fileToDownload.name}');
  final downloadTask = storageRef.writeToFile(targetFile);

  // 先下发带task引用的加载状态,支持后续取消操作
  emit(FileState(
    status: BlocStatus.loading,
    exists: false,
    task: downloadTask,
  ));

  try {
    // 等待下载任务执行完成
    await downloadTask;
    // 校验文件是否真实存在
    final isFileSaved = await targetFile.exists();
    emit(FileState(
      status: isFileSaved ? BlocStatus.loaded : BlocStatus.error,
      exists: isFileSaved,
      task: null, // 任务结束后清空task引用,避免内存泄漏
    ));
  } on FirebaseException catch (e) {
    // 捕获任务取消的特定异常,清理临时文件
    if (e.code == 'canceled') {
      if (await targetFile.exists()) {
        await targetFile.delete();
      }
      return;
    }
    // 处理其他下载错误
    emit(FileState(
      status: BlocStatus.error,
      exists: false,
      task: null,
      errorMsg: e.message ?? '下载失败',
    ));
  } catch (e) {
    emit(FileState(
      status: BlocStatus.error,
      exists: false,
      task: null,
      errorMsg: e.toString(),
    ));
  }
}

3. 修改UI层触发逻辑

找到代码中标记//here should be cancel functionality的位置,添加事件触发代码即可:

center: GestureDetector(
  onTap: () {
    // 向Bloc发送取消下载事件
    fileBloc.add(CancelDownloadEvent());
  },
  child: SizedBox(
    height: 10.5.h,
    width: 10.5.w,
    child: SvgPicture.asset(
      'assets/icons/cancel.svg',
      color: Palette.white,
    ),
  ),
),

注意事项

  • 不要在UI层直接持有task引用调用cancel方法,所有状态变更都通过Bloc事件分发,符合单向数据流规范
  • 取消任务后必须删除未下载完成的临时文件,否则下次下载时的文件存在性校验会出错
  • 下载任务结束(成功/失败/取消)后必须清空state中的task引用,避免内存泄漏
  • 原代码中CircularPercentIndicator的percent参数写死为1,如果需要显示真实下载进度,可以监听downloadTask.snapshotEvents获取实时下载字节数,计算进度后更新到state中供UI使用

内容的提问来源于stack exchange,提问作者Veronika Haichenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:31:11