Flutter BLoC提交时List状态丢失问题求助
问题描述
使用Flutter BLoC管理状态,ExamState中存储List<Map<String, dynamic>>类型的scores。提交数据时state.scores为空,但在HandleScoreInputEvent事件处理中能正常访问已设置的状态,已排查三天未解决。
相关代码
State代码
class ExamState { final List<Map<String, dynamic>> scores; ExamState({ this.scores = const [], }); ExamState copyWith({ List<Map<String, dynamic>> scores = const [], }) { return ExamState( scores: scores, ); } }
ExamBloc事件处理代码
List<Map<String, dynamic>>? scores; class ExamBloc extends Bloc<ExamEvent, ExamState> { final ExamRepository? repo = ExamRepository(); ExamBloc() : super(ExamState()) { on<HandleScoreInputEvent>((event, emit) { final existingIndex = state.scores .indexWhere((score) => score['studentId'] == event.studentId); if (existingIndex != -1) { final updatedScore = { ...state.scores[existingIndex], if (event.marks != null) 'marks': event.marks, if (event.comment != null) 'comment': event.comment, }; final List<Map<String, dynamic>> updatedScores = List.from(state.scores); updatedScores[existingIndex] = updatedScore; emit(state.copyWith(scores: updatedScores)); scores = updatedScores; } else { final score = { 'studentId': event.studentId, 'classId': event.classId, 'examId': event.examId, 'subjectId': event.subjectId, if (event.marks != null) 'marks': event.marks, if (event.comment != null) 'comment': event.comment, }; final updatedScores = [...state.scores, score]; updatedScores.add(score); emit(state.copyWith(scores: updatedScores)); } }); on<ShowExamEvent>((event, emit) async { emit(state.copyWith(formStatus: FormSubmitting())); try { final res = await repo!.showExam(examId: event.examId); if (res.statusCode == 200) { var responseJson = jsonDecode(res.body); ExamModel exam = ExamModel.fromJson(responseJson); emit(state.copyWith(exam: exam)); emit(state.copyWith(formStatus: SubmissionSuccess())); } else { emit(state.copyWith( formStatus: ApiRequestNotSuccessFul(res.body.toString()))); } } catch (e) { print(e); emit(state.copyWith(formStatus: SubmissionFailed(e as Exception))); } }); on<SubmitStudentExamResult>((event, emit) async { emit(state.copyWith(formStatus: FormSubmitting())); print(state.scores); // 此处为空 try { final res = await repo!.saveExamResult( data: scores, ); if (res.statusCode == 201) { emit(state.copyWith(formStatus: SubmissionSuccess())); } else { emit(state.copyWith( formStatus: ApiRequestNotSuccessFul(res.body.toString()))); } } catch (e) { emit(state.copyWith(formStatus: SubmissionFailed(e as Exception))); } }); } }
视图层防抖输入代码
final _debounceSubject = BehaviorSubject<String>(); @override void initState() { super.initState(); _debounceSubject.stream .debounceTime( Duration(milliseconds: 700)) // 可调整防抖时间 .listen((value) { // 处理防抖后的值(例如更新scores) // 仅当_currentStudentId可用时调用_handleMarksChange _handleMarksChange( value, // 此场景无评论 ); }); } List scores = []; String? _currentStudentId; void _handleMarksChange(String value) { // 更新BLoC状态中的新分数值 int? marksValue = int.tryParse(value); if (marksValue != null) { // 输入为分数数字 context.read<ExamBloc>().add(HandleScoreInputEvent( marks: int.parse(value), studentId: _currentStudentId!, classId: widget.classId, examId: widget.examId, subjectId: widget.subjectId, comment: null)); } else { context.read<ExamBloc>().add(HandleScoreInputEvent( marks: null, studentId: _currentStudentId!, classId: widget.classId, examId: widget.examId, subjectId: widget.subjectId, comment: value)); } } Widget _submitButton(BuildContext context) { return BlocBuilder<ExamBloc, ExamState>(builder: (context, state) { return PrimaryButton( onPressed: () { if (_key.currentState!.validate()) { context.read<ExamBloc>().add(SubmitStudentExamResult()); } }, label: state.formStatus is FormSubmitting ? CircularProgressIndicator( valueColor: const AlwaysStoppedAnimation<Color>(Colors.white), ) : const Text('Save'), ); }); }
问题根源
copyWith参数默认值错误:ExamState的copyWith方法中,scores默认值设为const [],导致调用copyWith不传入scores时,直接将新状态的scores重置为空数组(比如ShowExamEvent中更新exam时)。- 冗余的
add操作:HandleScoreInputEvent的else分支中,[...state.scores, score]已经完成新数据添加,又调用updatedScores.add(score),导致数据重复,且状态更新逻辑混乱。 - 违规使用全局变量:Bloc外部定义的
scores变量不符合BLoC状态管理规范,且仅在HandleScoreInputEvent的if分支赋值,else分支未处理,提交时依赖该变量而非state数据。
修复方案
1. 修复ExamState的copyWith方法
将参数默认值改为null,确保未传入时沿用当前状态的属性:
class ExamState { final List<Map<String, dynamic>> scores; final ExamModel? exam; final FormStatus formStatus; ExamState({ this.scores = const [], this.exam, this.formStatus = const InitialFormStatus(), }); ExamState copyWith({ List<Map<String, dynamic>>? scores, ExamModel? exam, FormStatus? formStatus, }) { return ExamState( scores: scores ?? this.scores, exam: exam ?? this.exam, formStatus: formStatus ?? this.formStatus, ); } }
2. 修正HandleScoreInputEvent逻辑
移除多余的add操作,删除全局变量赋值:
on<HandleScoreInputEvent>((event, emit) { final existingIndex = state.scores .indexWhere((score) => score['studentId'] == event.studentId); if (existingIndex != -1) { final updatedScore = { ...state.scores[existingIndex], if (event.marks != null) 'marks': event.marks, if (event.comment != null) 'comment': event.comment, }; final List<Map<String, dynamic>> updatedScores = List.from(state.scores); updatedScores[existingIndex] = updatedScore; emit(state.copyWith(scores: updatedScores)); } else { final score = { 'studentId': event.studentId, 'classId': event.classId, 'examId': event.examId, 'subjectId': event.subjectId, if (event.marks != null) 'marks': event.marks, if (event.comment != null) 'comment': event.comment, }; final updatedScores = [...state.scores, score]; emit(state.copyWith(scores: updatedScores)); } });
3. 修复提交事件逻辑
删除全局scores变量,直接使用state.scores提交数据:
on<SubmitStudentExamResult>((event, emit) async { emit(state.copyWith(formStatus: const FormSubmitting())); print(state.scores); // 现在可正常获取数据 try { final res = await repo!.saveExamResult( data: state.scores, ); if (res.statusCode == 201) { emit(state.copyWith(formStatus: const SubmissionSuccess())); } else { emit(state.copyWith( formStatus: ApiRequestNotSuccessFul(res.body.toString()))); } } catch (e) { emit(state.copyWith(formStatus: SubmissionFailed(e as Exception))); } });
内容的提问来源于stack exchange,提问作者wafutech
相关产品推荐
相关产品推荐

