Flutter调查应用提交时出现RangeError(index)问题求助
问题分析与修复方案
核心错误原因
- 提交循环提前中断并清空数据:在
AnswerSurvey的for循环中,第一次写入Firebase后立刻执行Navigator.pop(context)和_answerList.clear(),导致后续循环迭代时,_answerList已为空,访问_answerList[i]触发RangeError。 - 问题列表重复累积:ListView的itemBuilder内部的FutureBuilder每次完成都会往
_questionList添加数据,页面重建时会重复累加,导致列表长度与docIDs不匹配,后续访问_questionList[i]也可能越界。 - 无效空值判断:
_answerList != null完全无效,因为列表初始化时就是空列表,永远不会为null,应该判断列表长度与docIDs一致。
关键代码修复
1. 修复问题列表重复加载问题
将问题获取逻辑移到getDocID中,一次性加载所有数据,避免FutureBuilder重复添加:
Future getDocID() async { // 清空旧数据,防止重复加载 docIDs.clear(); _answerList.clear(); _questionList.clear(); final snapshot = await FirebaseFirestore.instance .collection('surveys/$docName/questions') .get(); for (var document in snapshot.docs) { docIDs.add(document.reference.id); _answerList.add(TextEditingController()); // 直接在这里获取问题并存入列表 Map<String, dynamic> data = document.data() as Map<String, dynamic>; _questionList.add(data['question']); } }
同时移除itemBuilder里的_questionList.add(data['question']);代码行。
2. 修复提交循环逻辑
将成功提示、页面跳转和资源清理放到循环结束后执行,避免提前中断:
AnswerSurvey() async { setState(() { pageIndex = 1; }); final user = FirebaseAuth.instance.currentUser; // 有效判断:用户存在且数据长度匹配 if (user != null && _answerList.length == docIDs.length) { print(docIDs); print(_questionList); print(_answerList); // 写入用户基础信息 await FirebaseFirestore.instance .collection('surveys/$docName/answers') .doc(user.uid) .set({ 'user email': user.email, }); // 循环写入所有答案 for (var i = 0; i < docIDs.length; i++) { try { await FirebaseFirestore.instance .collection('surveys/$docName/answers/${user.uid}/answers') .doc('option ${i.toString()}') .set({ 'question': _questionList[i], 'answer': _answerList[i].text, }); } catch (e) { showSnackBar('提交第${i+1}题失败: $e', context); print(e); continue; } } // 所有提交完成后统一处理 showSnackBar('SURVEY HAS BEEN SUBMITTED', context); // 释放控制器资源再清空 for (var controller in _answerList) { controller.dispose(); } _answerList.clear(); Navigator.pop(context); } else { showSnackBar('数据不完整或未登录', context); } setState(() { pageIndex = 2; }); }
3. 完善资源释放
修改dispose方法,确保控制器资源被正确释放:
@override void dispose() { // 逐个释放控制器资源 for (var controller in _answerList) { controller.dispose(); } _answerList.clear(); super.dispose(); }
额外优化建议
- 页面初始化时调用
getDocID(),避免FutureBuilder重复触发数据请求:
@override void initState() { super.initState(); getDocID(); }
- 移除嵌套的FutureBuilder,直接用已加载的
_questionList渲染问题,提升页面性能:
if (pageIndex == 2) { return Container( padding: EdgeInsets.only(top: 5), width: screenWidth, height: screenHeight * 0.65, color: Colors.white, child: docIDs.isEmpty ? Center(child: CircularProgressIndicator(color: EESBlue)) : ListView.builder( itemCount: docIDs.length, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.all(4.0), child: Container( alignment: Alignment.center, width: screenWidth, decoration: BoxDecoration( color: Colors.grey[300], boxShadow: [ BoxShadow( color: Color.fromRGBO(255, 255, 255, 0.25), offset: Offset(0, 4), blurRadius: 50), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 25.0, vertical: 10), child: TextWidget( text: _questionList[index], fontSize: 12, maxLines: 2, color: Colors.black, isHeading: true, ), ), GestureDetector( onTap: () {}, child: Padding( padding: const EdgeInsets.only(bottom: 15.0), child: Container( width: screenWidth * 0.55, decoration: BoxDecoration( color: buttonColor, borderRadius: BorderRadius.circular(5)), child: Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: TextField( controller: _answerList[index], style: textFliedHintStyle.copyWith(color: Colors.white), decoration: InputDecoration( hintText: 'ANSWER', hintStyle: textFliedHintStyle.copyWith(color: Colors.white)), maxLines: null, )), ), ), ), ), ], ), ), ); }, ), ); }
内容的提问来源于stack exchange,提问作者Cameron
相关产品推荐
相关产品推荐

