如何用Dio实现上传进度显示并结合Cubit更新UI?
Dio上传进度更新UI(Cubit架构实现)
针对你的场景,这里提供两种实用的实现方式,适配Cubit架构下Repository和UI的状态同步需求:
方式一:通过Stream传递进度
适合需要对进度流做额外处理(比如节流、多监听)的场景,步骤如下:
1. Repository层封装上传逻辑并暴露进度Stream
class UploadRepository { final ApiService _apiService = ApiService(); Stream<double> uploadFile(String url, FormData formData) { final progressController = StreamController<double>(); _apiService.dio.post( url, data: formData, onSendProgress: (count, total) { if (total > 0) { // 计算百分比进度,避免除以0 final progress = (count / total) * 100; progressController.add(progress); } }, ).then((_) { progressController.close(); }).catchError((error) { progressController.addError(error); progressController.close(); }); return progressController.stream; } }
2. Cubit层监听Stream并更新状态
// 定义上传状态类 abstract class UploadState {} class UploadInitial extends UploadState {} class UploadInProgress extends UploadState { final double progress; UploadInProgress({required this.progress}); } class UploadSuccess extends UploadState {} class UploadFailed extends UploadState { final String error; UploadFailed({required this.error}); } // Cubit实现 class UploadCubit extends Cubit<UploadState> { final UploadRepository _repository; StreamSubscription? _progressSubscription; UploadCubit(this._repository) : super(UploadInitial()); void startUpload(String url, FormData formData) { emit(UploadInProgress(progress: 0.0)); _progressSubscription = _repository.uploadFile(url, formData) .listen( (progress) => emit(UploadInProgress(progress: progress)), onError: (error) => emit(UploadFailed(error: error.toString())), onDone: () => emit(UploadSuccess()), ); } @override Future<void> close() { _progressSubscription?.cancel(); // 取消订阅防止内存泄漏 return super.close(); } }
3. UI层用BlocBuilder渲染进度
BlocBuilder<UploadCubit, UploadState>( builder: (context, state) { if (state is UploadInProgress) { return LinearProgressIndicator(value: state.progress / 100); } else if (state is UploadSuccess) { return const Text('上传完成'); } else if (state is UploadFailed) { return Text('上传失败:${state.error}'); } return ElevatedButton( onPressed: () { // 触发上传逻辑 context.read<UploadCubit>().startUpload(url, formData); }, child: const Text('开始上传'), ); }, )
方式二:通过回调函数传递进度
代码更简洁,适合单一监听的场景,无需额外维护Stream:
1. Repository层添加进度回调参数
class UploadRepository { final ApiService _apiService = ApiService(); Future<void> uploadFile( String url, FormData formData, Function(double) onProgress, // 进度回调 ) async { try { await _apiService.dio.post( url, data: formData, onSendProgress: (count, total) { if (total > 0) { final progress = (count / total) * 100; onProgress(progress); } }, ); } catch (e) { rethrow; } } }
2. Cubit层通过回调更新状态
class UploadCubit extends Cubit<UploadState> { final UploadRepository _repository; UploadCubit(this._repository) : super(UploadInitial()); Future<void> startUpload(String url, FormData formData) async { try { emit(UploadInProgress(progress: 0.0)); await _repository.uploadFile( url, formData, (progress) => emit(UploadInProgress(progress: progress)), ); emit(UploadSuccess()); } catch (e) { emit(UploadFailed(error: e.toString())); } } }
UI层代码和方式一完全一致,无需修改。
注意事项
- 处理
total=0的边界情况:部分服务器不返回文件总大小,此时可隐藏进度指示器或显示"上传中..."的提示 - Cubit的
close方法必须取消Stream订阅(方式一),避免内存泄漏 - 状态类要清晰区分不同阶段,确保UI能正确响应状态变化
内容的提问来源于stack exchange,提问作者As Kira
相关产品推荐
相关产品推荐

