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

Flutter单选/多选选择题功能中多选答案返回null的问题求助

Flutter单选/多选选择题功能中多选答案返回null的问题求助

嗨,我看了你的代码,发现几个关键问题导致多选时返回null,咱们一步步来解决:

首先说最核心的错误——你用了RadioListTile来做多选!这玩意儿是单选组件啊,选了一个就会取消另一个,根本没法实现多选,这肯定会导致状态混乱,最后拿不到正确的选中值。

然后是状态管理的问题:你用String _selection来存多选的选中项,这完全不对——多选需要存多个Answers对象,单个字符串不仅类型不匹配,也根本装不下多个选项,这也是返回null的重要原因。

还有你的_answers列表初始化的是List<Answers?>,但多选问题需要存储的是多个答案,不是单个,所以数据结构也得调整。

另外,你这段代码逻辑完全错误,会导致答案重复:

if (options != null && options.contains(question.answers)) {
  options.addAll(question.answers!);
}

options本身就是question.answers,contains是判断元素是否在列表里,而question.answers是List,所以这个条件永远不成立,但就算成立,也会把答案重复添加一遍,比如原来2个答案变成4个,这明显是bug,直接删掉就行。

最后你的Answers类里的contains、add、remove方法写得不对,这些方法应该是针对List的,你写在单个Answers对象里完全没用,还会误导逻辑。

接下来给你修复后的完整代码,我会标注关键修改点:

1. 调整状态变量

int _currentIndex = 0;
// 用dynamic适配单选(存单个Answers)和多选(存List<Answers>)的情况
List<dynamic> _answers = [];
// 临时存储当前多选问题的选中项
List<Answers> _multiSelected = [];

2. 初始化状态

@override
void initState() {
  super.initState();
  // 根据问题类型初始化答案容器:单选为null,多选为空列表
  _answers = List.generate(widget.questions.length, (index) {
    if (widget.questions[index].isMultiChoice == true) {
      return <Answers>[];
    } else {
      return null;
    }
  });
}

3. 修复Build方法逻辑

import 'dart:convert';
import 'package:flutter/services.dart';

@override
Widget build(BuildContext context) {
  var size = MediaQuery.of(context).size;
  Question question = widget.questions[_currentIndex];
  final List<Answers>? options = question.answers;

  // 切换到多选问题时,从_answers恢复之前的选中状态
  if (question.isMultiChoice == true) {
    _multiSelected = List.from(_answers[_currentIndex] as List<Answers>);
  }

  return Card(
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        // 新增:显示问题内容(你原来的代码里没加,建议加上)
        Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(
            question.content ?? '',
            style: TextStyle(fontSize: size.width > 800 ? 32.0 : 18.0),
          ),
        ),
        if (options != null)
          ...options.map((answer) {
            if (question.isMultiChoice == true) {
              // 多选改用CheckboxListTile
              return CheckboxListTile(
                title: Text(
                  HtmlUnescape().convert("${answer.content}"),
                  style: size.width > 800
                      ? TextStyle(fontSize: 30.0)
                      : null,
                ),
                // 用重写后的==判断是否选中
                value: _multiSelected.contains(answer),
                activeColor: Colors.blue,
                onChanged: (bool? val) {
                  setState(() {
                    if (val == true) {
                      _multiSelected.add(answer);
                    } else {
                      // 通过id匹配删除选中项
                      _multiSelected.removeWhere((item) => item.id == answer.id);
                    }
                    // 更新_answers中当前问题的选中数据
                    _answers[_currentIndex] = _multiSelected;
                  });
                },
              );
            } else {
              // 单选保留RadioListTile,修正groupValue绑定
              return RadioListTile<Answers>(
                title: Text(
                  HtmlUnescape().convert("${answer.content}"),
                  style: size.width > 800
                      ? TextStyle(fontSize: 30.0)
                      : null,
                ),
                groupValue: _answers[_currentIndex] as Answers?,
                value: answer,
                onChanged: (Answers? value) {
                  setState(() {
                    _answers[_currentIndex] = value;
                  });
                },
              );
            }
          }).toList(),
        // 新增:切换问题和提交按钮,方便测试和提交数据
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_currentIndex > 0)
              TextButton(
                onPressed: () => setState(() => _currentIndex--),
                child: const Text('上一题'),
              ),
            if (_currentIndex < widget.questions.length - 1)
              TextButton(
                onPressed: () => setState(() => _currentIndex++),
                child: const Text('下一题'),
              ),
            TextButton(
              onPressed: () {
                // 生成符合API要求的提交数据
                List<Map<String, dynamic>> submitData = [];
                for (int i = 0; i < widget.questions.length; i++) {
                  Question q = widget.questions[i];
                  dynamic answerData = _answers[i];
                  List<int> answerIds = [];
                  
                  if (q.isMultiChoice == true) {
                    // 多选时提取所有选中答案的id
                    List<Answers> selectedAnswers = answerData as List<Answers>;
                    answerIds = selectedAnswers.map((a) => a.id!).toList();
                  } else {
                    // 单选时提取单个答案的id
                    Answers? selectedAnswer = answerData as Answers?;
                    if (selectedAnswer != null) {
                      answerIds.add(selectedAnswer.id!);
                    }
                  }
                  
                  submitData.add({
                    "questionId": q.id,
                    "answerId": answerIds,
                  });
                }
                // 打印提交数据,确认格式正确
                print('提交数据:${json.encode(submitData)}');
                // 这里替换成你的API提交逻辑
              },
              child: const Text('提交'),
            ),
          ],
        ),
      ],
    ),
  );
}

4. 修复Answers类

重写相等判断方法,让contains能正确识别同一个答案(通过id匹配),删掉没用的方法:

class Answers {
  int? id;
  String? content;
  bool? isCorrect;

  Answers({this.id, this.content, this.isCorrect});

  Answers.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    content = json['content'];
    isCorrect = json['isCorrect'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = Map<String, dynamic>();
    data['id'] = this.id;
    data['content'] = this.content;
    data['isCorrect'] = this.isCorrect;
    return data;
  }

  // 重写==和hashCode,用于判断两个答案是否为同一个(通过id)
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Answers &&
          runtimeType == other.runtimeType &&
          id == other.id;

  @override
  int get hashCode => id.hashCode;
}

关键修复点总结

  • 组件替换:多选用CheckboxListTile替代RadioListTile,这是实现多选的基础
  • 状态适配:用List<dynamic>存储不同类型的答案,多选时用List<Answers>保存多个选中项
  • 逻辑清理:删除重复添加答案的无效代码,避免列表重复
  • 相等判断:重写Answers的==方法,确保多选时能正确识别选中状态
  • 提交格式化:遍历_answers生成符合API要求的提交数据结构

这样修改后,多选时就能正确保存选中的答案,提交时也能生成你需要的answerId数组,不会再出现null的情况了。

备注:内容来源于stack exchange,提问作者tailn323

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 15:39:29