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

Flutter嵌套ListView实现单选项复选框(每题限选一个答案)

在Flutter嵌套ListView中实现每题仅选一个复选框

你当前代码的问题在于用了全局的isSelected变量,所有复选框共享同一个状态,导致点任何一个都会全选/全消。要实现每题单选,得给每个问题单独维护选中状态,具体修改方案如下:

核心修改点

  • 替换全局状态:用Map<int, int?>存储每个问题对应的选中选项ID(key为问题ID,value为选中的选项ID)
  • 调整选中回调:需要传入问题ID和选项ID,精准更新对应问题的选中状态
  • 修正复选框的value判断:根据当前问题和选项,判断是否为选中项

修改后的完整代码

class SurveyPage extends StatefulWidget {
  const SurveyPage({super.key});

  @override
  State<SurveyPage> createState() => _SurveyPageState();
}

class _SurveyPageState extends State<SurveyPage> {
  // 存储每个问题对应的选中选项ID,key=问题ID,value=选中的选项ID
  Map<int, int?> selectedAnswers = {};

  void onSelected(int questionId, int optionId) {
    setState(() {
      selectedAnswers[questionId] = optionId;
    });
  }

  List<dynamic> survey = [
    {
      "id": 1,
      "question": "Question1",
      "answer": [
        {"id": 1, "name": "Option A"},
        {"id": 2, "name": "Option B"},
        {"id": 3, "name": "Option C"},
        {"id": 4, "name": "Option D"}
      ],
    },
    {
      "id": 2,
      "question": "Question2",
      "answer": [
        {"id": 1, "name": "Option A"},
        {"id": 2, "name": "Option B"},
        {"id": 3, "name": "Option C"},
        {"id": 4, "name": "Option D"}
      ],
    },
  ];

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      shrinkWrap: true,
      itemCount: survey.length,
      itemBuilder: (BuildContext context, int index) {
        final question = survey[index];
        final questionId = question["id"] as int;
        final listAnswer = question["answer"] as List<dynamic>;
        return Container(
          padding: const EdgeInsets.all(10),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                width: MediaQuery.of(context).size.width * 0.9,
                child: Text(
                  question["question"] as String,
                  maxLines: 5,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
              const SizedBox(height: 10),
              ListView.builder(
                shrinkWrap: true,
                physics: const NeverScrollableScrollPhysics(), // 解决嵌套滚动冲突
                itemCount: listAnswer.length,
                // 内层index改名避免和外层冲突
                itemBuilder: (BuildContext context, int answerIndex) {
                  final option = listAnswer[answerIndex];
                  final optionId = option["id"] as int;
                  return Container(
                    margin: const EdgeInsets.only(bottom: 10),
                    child: ListTile(
                      title: Text(option["name"] as String),
                      trailing: Checkbox(
                        checkColor: Colors.white,
                        // 判断当前选项是否为该问题的选中项
                        value: selectedAnswers[questionId] == optionId,
                        shape: const CircleBorder(),
                        onChanged: (bool? value) {
                          if (value == true) {
                            onSelected(questionId, optionId);
                          } else {
                            // 可选:允许取消选中,若不需要则删除else分支
                            setState(() {
                              selectedAnswers[questionId] = null;
                            });
                          }
                        },
                      ),
                    ),
                  );
                },
              )
            ],
          ),
        );
      },
    );
  }
}

额外说明

  • 添加physics: const NeverScrollableScrollPhysics()是为了避免嵌套ListView的滚动事件冲突,确保外层ListView正常滚动
  • 如果不需要允许用户取消选中,直接删除onChanged中的else分支即可,此时用户只能在同一问题的选项间切换,无法清空选择

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 04:20:57