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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 04:07:13