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

Flutter Riverpod:FutureProvider.autoDispose.family上传逻辑实现问询

解决方案与示例代码

问题1:避免未监听时Provider被销毁导致上传终止

FutureProvider.autoDispose.family默认会在无监听者时立即销毁,触发onDispose回调中断上传。解决思路是通过ref.keepAlive()让Provider在上传任务完成前保持存活,同时优化取消逻辑,仅在上传未完成时触发取消:

  • 调用ref.keepAlive()绑定Provider生命周期至上传任务结束,避免无监听时被销毁
  • 在onDispose中判断上传状态,仅当任务未完成时触发cancelToken

问题2:保证watch与read的filePath一致

无需额外定义成员变量,只需在触发上传时固定当前的文件路径值,用同一个变量调用Provider即可。若需监听上传状态,建议使用独立的StateProvider跟踪状态,而非直接watch上传Provider,彻底规避路径不一致风险。


完整示例代码

1. 定义状态与核心Provider

// 跟踪上传状态(用于UI反馈,替代直接watch上传Provider)
final uploadStatusProvider = StateProvider<UploadStatus>((ref) => UploadStatus.idle);

enum UploadStatus {
  idle,
  loading,
  success,
  failed,
}

// 上传业务Provider
final uploadProvider = FutureProvider.autoDispose.family<RetData, String>((ref, filePath) async {
  Logger.instance.d("上传开始:$filePath");
  final cancelToken = MyCancelToken();
  final noteRepository = ref.watch(noteRepositoryProvider);

  // 让Provider保持存活直到上传任务完成
  final keepAliveLink = ref.keepAlive();
  // 任务完成后自动释放Provider,避免内存泄漏
  Future.microtask(() => keepAliveLink.close());

  ref.onDispose(() {
    // 仅当上传未完成时取消请求
    if (!cancelToken.isCancelled) {
      Logger.instance.d("上传被取消:$filePath");
      cancelToken.cancel();
    }
  });

  try {
    final retImport = await noteRepository.upload(filePath, cancelToken);
    return retImport;
  } catch (e) {
    // 单独捕获取消异常,区分主动取消与其他错误
    if (e is CancelException) rethrow;
    throw e;
  }
});

2. 页面上传逻辑实现

// 假设selectedFilePath为页面获取到的动态文件路径
void _handleUploadPressed() async {
  // 固定当前路径,避免后续状态变化导致路径不一致
  final currentFilePath = selectedFilePath;
  if (currentFilePath.isEmpty) return;

  final ref = context.read;
  ref(uploadStatusProvider.notifier).state = UploadStatus.loading;

  try {
    // 使用固定路径调用Provider,确保read与内部逻辑的路径完全一致
    final ret = await ref(uploadProvider(currentFilePath).future);
    ref(uploadStatusProvider.notifier).state = UploadStatus.success;
    Toast.show("上传成功");
  } on CancelException {
    ref(uploadStatusProvider.notifier).state = UploadStatus.idle;
    Toast.show("上传已取消");
  } catch (e) {
    ref(uploadStatusProvider.notifier).state = UploadStatus.failed;
    Toast.show("上传失败: ${e.toString()}");
  }
}

// 页面UI构建(监听上传状态,控制按钮状态)
@override
Widget build(BuildContext context) {
  final uploadStatus = context.watch(uploadStatusProvider);
  return Scaffold(
    body: Center(
      child: ElevatedButton(
        onPressed: uploadStatus == UploadStatus.loading ? null : _handleUploadPressed,
        child: const Text("上传文件"),
      ),
    ),
  );
}

关键说明

  1. ref.keepAlive()的作用:

    • 调用后Provider不会因无监听者立即销毁,会持续存在至keepAliveLink.close()被调用,确保上传任务完整执行
    • 用Future.microtask在任务完成后关闭链接,保证Provider及时释放,避免内存泄漏
  2. 路径一致性保障:

    • 触发上传时将动态路径赋值给局部变量currentFilePath,后续所有Provider调用均使用该固定值,彻底消除路径不一致的可能
    • 用独立的StateProvider跟踪上传状态,无需直接watch上传Provider,进一步降低风险
  3. 取消逻辑优化:

    • 在onDispose中判断cancelToken状态,避免上传正常完成后误触发取消操作
    • 单独捕获取消异常,精准区分用户主动取消与其他上传错误

内容的提问来源于stack exchange,提问作者jiaming zhu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:22:41