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
相关产品推荐
相关产品推荐

